- commit
- 9d1d263e4156c77401114c863d78be382de30611
- parent
- a9392796260aac77a1e871ec1a77bbd6d76e9faf
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 5 | +++-- |
| M | src/ | 7 | +++++++ |
| M | src/ | 14 | ++++++++++++++ |
| M | src/ | 2 | ++ |
| M | src/ | 2 | +- |
| M | src/ | 8 | ++++++-- |
@@ -1,5 +1,5 @@
11 import { levels as builtInLevels } from './levels.js';2-import { MOD_SCHEMA_VERSION, validateMod } from './mods.js';2+import { LEGACY_RULES_VERSION, MOD_SCHEMA_VERSION, validateMod } from './mods.js';33 import { cloneData } from './data/clone.js';44 55 export const CORE_PACK_ID = 'core';@@ -21,7 +21,7 @@ function fail(errors) {
2121 */2222 export function createCorePack(sourceLevels = builtInLevels) {2323 return {24- manifest: { id: CORE_PACK_ID, name: 'Miau Core', version: '1.0.0', engine: MOD_SCHEMA_VERSION },24+ manifest: { id: CORE_PACK_ID, name: 'Miau Core', version: '1.0.0', engine: MOD_SCHEMA_VERSION, rules: 2 },2525 levels: cloneData(sourceLevels),2626 palette: {},2727 vocabulary: [],@@ -50,6 +50,7 @@ function validatePack(pack) {
5050 function makePackRecord(pack) {5151 validatePack(pack);5252 const normalized = cloneData(pack);53+ normalized.manifest.rules ??= LEGACY_RULES_VERSION;5354 const packId = normalized.manifest.id;5455 const entries = normalized.levels.map(level => {5556 const localId = level.id;@@ -11,6 +11,8 @@ export { collectModWarnings, MAX_MOD_WARNINGS } from './mods/diagnostics.js';
1111 */1212 1313 export const MOD_SCHEMA_VERSION = 1;14+export const CURRENT_RULES_VERSION = 2;15+export const LEGACY_RULES_VERSION = 1;1416 export const MAX_MOD_SOURCE_LENGTH = 512 * 1024;1517 export const MAX_MOD_LEVELS = 64;1618 export const MAX_LEVEL_OBJECTS = 4096;@@ -217,6 +219,10 @@ export function validateMod(mod) {
217219 if (manifest.forkOf !== undefined && (typeof manifest.forkOf !== 'string' || !ID_RE.test(manifest.forkOf))) errors.push(issue('manifest.forkOf', 'must be the original pack id'));218220 if (typeof manifest.version !== 'string' || !VERSION_RE.test(manifest.version)) errors.push(issue('manifest.version', 'must be a semantic version such as 1.0.0'));219221 if (manifest.engine !== undefined && manifest.engine !== MOD_SCHEMA_VERSION) errors.push(issue('manifest.engine', `must be ${MOD_SCHEMA_VERSION} for this loader`));222+ if (manifest.rules !== undefined) {223+ if (!isInteger(manifest.rules) || manifest.rules < 1) errors.push(issue('manifest.rules', 'must be a positive integer no greater than 2'));224+ else if (manifest.rules > CURRENT_RULES_VERSION) errors.push(issue('manifest.rules', `unsupported future rules revision ${manifest.rules}; host supports ${CURRENT_RULES_VERSION}`));225+ }220226 }221227 if (manifest && manifest.features !== undefined) {222228 if (!Array.isArray(manifest.features)) errors.push(issue('manifest.features', 'must be an array of unique lowercase feature identifiers'));@@ -257,6 +263,7 @@ export function validateMod(mod) {
257263 function normalizeMod(mod) {258264 const normalized = copy(mod);259265 normalized.manifest.engine = normalized.manifest.engine ?? MOD_SCHEMA_VERSION;266+ normalized.manifest.rules = normalized.manifest.rules ?? LEGACY_RULES_VERSION;260267 normalized.levels = normalized.levels.map(level => ({261268 ...level,262269 ...(Array.isArray(level.automata) ? { automata: level.automata.map(automaton => ({@@ -1,5 +1,6 @@
11 import { parseRules, RuleParseLimitError } from '../engine/rules.js';22 import { CORE_PROPERTY_TOKENS } from '../engine/tokens.js';3+import { CURRENT_RULES_VERSION } from '../mods.js';34 45 export const MAX_MOD_WARNINGS = 64;56 const CORE_PROPERTIES = new Set(CORE_PROPERTY_TOKENS);@@ -9,6 +10,19 @@ const isRecord = value => value !== null && typeof value === 'object' && !Array.
910 export function collectModWarnings(normalizedMod) {1011 if (!isRecord(normalizedMod)) return [];1112 const warnings = [];13+ const manifest = normalizedMod.manifest;14+ if (isRecord(manifest) && Object.prototype.hasOwnProperty.call(manifest, 'rules')) {15+ const rules = Number.isInteger(manifest.rules) ? manifest.rules : 1;16+ if (rules < CURRENT_RULES_VERSION) warnings.push({17+ word: 'RULES',18+ levelId: null,19+ levelTitle: null,20+ sentence: null,21+ message: 'This pack was made for older rules; some proofs may play differently.',22+ detail: `manifest.rules: effective revision ${rules}; current host revision ${CURRENT_RULES_VERSION}.`,23+ });24+ }25+ if (warnings.length >= MAX_MOD_WARNINGS) return warnings;1226 const levels = Array.isArray(normalizedMod.levels) ? normalizedMod.levels : [];1327 const allProperties = new Set(CORE_PROPERTIES);1428 const packNouns = new Set(Array.isArray(normalizedMod.vocabulary) ? normalizedMod.vocabulary.filter(value => typeof value === 'string').map(value => value.toUpperCase()) : []);@@ -61,6 +61,7 @@ function manifestFromView(view) {
6161 name: view.name,6262 ...(view.author !== undefined && view.author !== null ? { author: view.author } : {}),6363 version: view.version || '1.0.0',64+ rules: view.rules ?? 1,6465 ...(view.engine !== undefined ? { engine: view.engine } : {}),6566 ...(Array.isArray(view.features) ? { features: cloneData(view.features) } : {}),6667 ...(view.forkOf !== undefined && view.forkOf !== null ? { forkOf: view.forkOf } : {}),@@ -89,6 +90,7 @@ function createPackView(catalog, source, origin, previews, conflict = null) {
8990 name: data.manifest.name,9091 author: data.manifest.author ?? null,9192 version: data.manifest.version,93+ rules: data.manifest.rules ?? 1,9294 engine: data.manifest.engine,9395 features: Array.isArray(data.manifest.features) ? cloneData(data.manifest.features) : null,9496 forkOf: data.manifest.forkOf ?? null,@@ -182,7 +182,7 @@ export function createIndexLayout(source) {
182182 }183183 }184184 }185- const worlds = groups.map((group, index) => ({185+ const worlds = groups.map(({ entries: _entries, ...group }, index) => ({186186 ...group,187187 number: index + 1,188188 previousWorldId: groups[index - 1]?.id || null,@@ -5,7 +5,7 @@
55 * decodeSharedPack(payload) -> validated, normalized mod data (never executes JS)66 * exportCommonJS(pack) -> deterministic CommonJS source containing only data77 */8-import { validateMod, MOD_SCHEMA_VERSION } from './mods.js';8+import { validateMod, LEGACY_RULES_VERSION, MOD_SCHEMA_VERSION } from './mods.js';99 import { cloneJsonData } from './data/clone.js';1010 1111 export const SHARE_SCHEMA_VERSION = 1;@@ -32,7 +32,11 @@ function byteLength(text) { return new TextEncoder().encode(text).byteLength; }
3232 function normalizePack(pack) {3333 const copy = cloneJsonData(pack, 'shared pack');3434 if (!isObject(copy)) throw new TypeError('shared pack must be an object');35- copy.manifest = { ...copy.manifest, engine: copy.manifest?.engine ?? MOD_SCHEMA_VERSION };35+ copy.manifest = {36+ ...copy.manifest,37+ engine: copy.manifest?.engine ?? MOD_SCHEMA_VERSION,38+ rules: copy.manifest?.rules ?? LEGACY_RULES_VERSION,39+ };3640 copy.levels = Array.isArray(copy.levels) ? copy.levels.map(level => ({3741 ...level,3842 ...(Array.isArray(level.automata) ? { automata: level.automata.map(automaton => ({