@patterkit/core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +23 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +24 -3
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -167,6 +167,12 @@ function validateProject(input) {
|
|
|
167
167
|
const sceneGameIds = /* @__PURE__ */ new Map();
|
|
168
168
|
const jumps = [];
|
|
169
169
|
const castNames = new Set((project.cast ?? []).map((c) => c.name));
|
|
170
|
+
const qualifierIds = new Set((0, import_model.projectQualifiers)(project).map((q) => q.gameId));
|
|
171
|
+
const checkQualifier = (beat, what) => {
|
|
172
|
+
if (beat.kind === "line" && beat.qualifier !== void 0 && !qualifierIds.has(beat.qualifier)) {
|
|
173
|
+
issues.push({ code: "unknown-qualifier", message: `${what} '${beat.id}' qualifier '${beat.qualifier}' is not in the project's qualifiers`, id: beat.id });
|
|
174
|
+
}
|
|
175
|
+
};
|
|
170
176
|
const checkTags = (tags, where, id) => {
|
|
171
177
|
for (const t of tags ?? []) {
|
|
172
178
|
if (typeof t !== "string" || t.length === 0 || /[\s,]/.test(t)) {
|
|
@@ -174,6 +180,7 @@ function validateProject(input) {
|
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
182
|
};
|
|
183
|
+
const addressOf = (node) => (0, import_model2.effectiveGameId)({ gameId: node.gameId, name: typeof node.name === "string" ? node.name : "" });
|
|
177
184
|
const seeId = (id, kind, where) => {
|
|
178
185
|
if (typeof id !== "string" || id.length === 0) {
|
|
179
186
|
issues.push({ code: "missing-id", message: `${kind} in ${where} has no id` });
|
|
@@ -198,7 +205,7 @@ function validateProject(input) {
|
|
|
198
205
|
if (scene.gameId && !(0, import_model2.isValidGameId)(scene.gameId)) {
|
|
199
206
|
issues.push({ code: "invalid-gameid", message: `${where} address '${scene.gameId}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)`, id: scene.id });
|
|
200
207
|
}
|
|
201
|
-
const sgid = (
|
|
208
|
+
const sgid = addressOf(scene);
|
|
202
209
|
if (sgid) {
|
|
203
210
|
const prev = sceneGameIds.get(sgid);
|
|
204
211
|
if (prev && prev !== scene.id) issues.push({ code: "duplicate-gameid", message: `${where} address '${sgid}' is already used by scene '${prev}'`, id: scene.id });
|
|
@@ -233,7 +240,7 @@ function validateProject(input) {
|
|
|
233
240
|
if (block.gameId && !(0, import_model2.isValidGameId)(block.gameId)) {
|
|
234
241
|
issues.push({ code: "invalid-gameid", message: `block '${block.id}' address '${block.gameId}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)`, id: block.id });
|
|
235
242
|
}
|
|
236
|
-
const bgid = (
|
|
243
|
+
const bgid = addressOf(block);
|
|
237
244
|
if (bgid) {
|
|
238
245
|
const prev = blockGameIds.get(bgid);
|
|
239
246
|
if (prev && prev !== block.id) issues.push({ code: "duplicate-gameid", message: `block '${block.id}' address '${bgid}' is already used by block '${prev}' in ${where}`, id: block.id });
|
|
@@ -289,6 +296,7 @@ function validateProject(input) {
|
|
|
289
296
|
if (p.kind === "line" && p.character && !castNames.has(p.character)) {
|
|
290
297
|
issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
|
|
291
298
|
}
|
|
299
|
+
checkQualifier(p, "prompt");
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
302
|
if (fallbacks > 1) {
|
|
@@ -318,6 +326,7 @@ function validateProject(input) {
|
|
|
318
326
|
id: beat.id
|
|
319
327
|
});
|
|
320
328
|
}
|
|
329
|
+
checkQualifier(beat, "beat");
|
|
321
330
|
}
|
|
322
331
|
if (node.jump) jumps.push({ to: node.jump.to, from: node.id });
|
|
323
332
|
});
|
|
@@ -390,6 +399,18 @@ function validateProjectFile(project, issues) {
|
|
|
390
399
|
if (project.recordingStatuses) {
|
|
391
400
|
checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
|
|
392
401
|
}
|
|
402
|
+
const seenQualifiers = /* @__PURE__ */ new Set();
|
|
403
|
+
for (const q of project.qualifiers ?? []) {
|
|
404
|
+
if (typeof q.name !== "string" || !q.name.trim()) {
|
|
405
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.gameId)}' has no name` });
|
|
406
|
+
}
|
|
407
|
+
if (typeof q.gameId !== "string" || !(0, import_model2.isValidGameId)(q.gameId)) {
|
|
408
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.name)}' address '${String(q.gameId)}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)` });
|
|
409
|
+
} else if (seenQualifiers.has(q.gameId)) {
|
|
410
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier address '${q.gameId}' is used more than once` });
|
|
411
|
+
}
|
|
412
|
+
if (typeof q.gameId === "string") seenQualifiers.add(q.gameId);
|
|
413
|
+
}
|
|
393
414
|
for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
|
|
394
415
|
const seen = /* @__PURE__ */ new Set();
|
|
395
416
|
for (const f of fields ?? []) {
|
package/dist/index.d.cts
CHANGED
|
@@ -26,7 +26,7 @@ interface StringifyOptions {
|
|
|
26
26
|
declare function canonicalStringify(value: unknown, opts?: StringifyOptions): string;
|
|
27
27
|
|
|
28
28
|
interface ValidationIssue {
|
|
29
|
-
code: "duplicate-id" | "missing-id" | "malformed-node" | "missing-name" | "empty-scene" | "empty-snippet" | "empty-container" | "dangling-jump" | "jump-into-non-addressable" | "invalid-gameid" | "duplicate-gameid" | "unknown-character" | "invalid-temporary" | "invalid-declaration" | "invalid-status-ladder" | "invalid-locales" | "invalid-captions" | "missing-prompt" | "invalid-prompt" | "multiple-fallbacks" | "choice-can-empty" | "invalid-status-value" | "unknown-doc-class" | "invalid-tag" | "invalid-gamedata-field" | "jump-cycle";
|
|
29
|
+
code: "duplicate-id" | "missing-id" | "malformed-node" | "missing-name" | "empty-scene" | "empty-snippet" | "empty-container" | "dangling-jump" | "jump-into-non-addressable" | "invalid-gameid" | "duplicate-gameid" | "unknown-character" | "unknown-qualifier" | "invalid-qualifier" | "invalid-temporary" | "invalid-declaration" | "invalid-status-ladder" | "invalid-locales" | "invalid-captions" | "missing-prompt" | "invalid-prompt" | "multiple-fallbacks" | "choice-can-empty" | "invalid-status-value" | "unknown-doc-class" | "invalid-tag" | "invalid-gamedata-field" | "jump-cycle";
|
|
30
30
|
message: string;
|
|
31
31
|
/** Id of the offending node/beat, where applicable. */
|
|
32
32
|
id?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ interface StringifyOptions {
|
|
|
26
26
|
declare function canonicalStringify(value: unknown, opts?: StringifyOptions): string;
|
|
27
27
|
|
|
28
28
|
interface ValidationIssue {
|
|
29
|
-
code: "duplicate-id" | "missing-id" | "malformed-node" | "missing-name" | "empty-scene" | "empty-snippet" | "empty-container" | "dangling-jump" | "jump-into-non-addressable" | "invalid-gameid" | "duplicate-gameid" | "unknown-character" | "invalid-temporary" | "invalid-declaration" | "invalid-status-ladder" | "invalid-locales" | "invalid-captions" | "missing-prompt" | "invalid-prompt" | "multiple-fallbacks" | "choice-can-empty" | "invalid-status-value" | "unknown-doc-class" | "invalid-tag" | "invalid-gamedata-field" | "jump-cycle";
|
|
29
|
+
code: "duplicate-id" | "missing-id" | "malformed-node" | "missing-name" | "empty-scene" | "empty-snippet" | "empty-container" | "dangling-jump" | "jump-into-non-addressable" | "invalid-gameid" | "duplicate-gameid" | "unknown-character" | "unknown-qualifier" | "invalid-qualifier" | "invalid-temporary" | "invalid-declaration" | "invalid-status-ladder" | "invalid-locales" | "invalid-captions" | "missing-prompt" | "invalid-prompt" | "multiple-fallbacks" | "choice-can-empty" | "invalid-status-value" | "unknown-doc-class" | "invalid-tag" | "invalid-gamedata-field" | "jump-cycle";
|
|
30
30
|
message: string;
|
|
31
31
|
/** Id of the offending node/beat, where applicable. */
|
|
32
32
|
id?: string;
|
package/dist/index.js
CHANGED
|
@@ -55,7 +55,7 @@ ${indent}}`;
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// src/validate.ts
|
|
58
|
-
import { walkNodes, DEFAULT_WRITING_STATUSES, DEFAULT_RECORDING_STATUSES, DEFAULT_DOCUMENTATION_CLASSES } from "@patterkit/model";
|
|
58
|
+
import { walkNodes, DEFAULT_WRITING_STATUSES, DEFAULT_RECORDING_STATUSES, DEFAULT_DOCUMENTATION_CLASSES, projectQualifiers } from "@patterkit/model";
|
|
59
59
|
import { isValidGameId, effectiveGameId } from "@patterkit/model";
|
|
60
60
|
import { isValidPropertyName, propertyNameify, RESERVED_PROPERTY_NAMES } from "@patterkit/model";
|
|
61
61
|
function choiceLoopsBack(choice, ...targets) {
|
|
@@ -129,6 +129,12 @@ function validateProject(input) {
|
|
|
129
129
|
const sceneGameIds = /* @__PURE__ */ new Map();
|
|
130
130
|
const jumps = [];
|
|
131
131
|
const castNames = new Set((project.cast ?? []).map((c) => c.name));
|
|
132
|
+
const qualifierIds = new Set(projectQualifiers(project).map((q) => q.gameId));
|
|
133
|
+
const checkQualifier = (beat, what) => {
|
|
134
|
+
if (beat.kind === "line" && beat.qualifier !== void 0 && !qualifierIds.has(beat.qualifier)) {
|
|
135
|
+
issues.push({ code: "unknown-qualifier", message: `${what} '${beat.id}' qualifier '${beat.qualifier}' is not in the project's qualifiers`, id: beat.id });
|
|
136
|
+
}
|
|
137
|
+
};
|
|
132
138
|
const checkTags = (tags, where, id) => {
|
|
133
139
|
for (const t of tags ?? []) {
|
|
134
140
|
if (typeof t !== "string" || t.length === 0 || /[\s,]/.test(t)) {
|
|
@@ -136,6 +142,7 @@ function validateProject(input) {
|
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
144
|
};
|
|
145
|
+
const addressOf = (node) => effectiveGameId({ gameId: node.gameId, name: typeof node.name === "string" ? node.name : "" });
|
|
139
146
|
const seeId = (id, kind, where) => {
|
|
140
147
|
if (typeof id !== "string" || id.length === 0) {
|
|
141
148
|
issues.push({ code: "missing-id", message: `${kind} in ${where} has no id` });
|
|
@@ -160,7 +167,7 @@ function validateProject(input) {
|
|
|
160
167
|
if (scene.gameId && !isValidGameId(scene.gameId)) {
|
|
161
168
|
issues.push({ code: "invalid-gameid", message: `${where} address '${scene.gameId}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)`, id: scene.id });
|
|
162
169
|
}
|
|
163
|
-
const sgid =
|
|
170
|
+
const sgid = addressOf(scene);
|
|
164
171
|
if (sgid) {
|
|
165
172
|
const prev = sceneGameIds.get(sgid);
|
|
166
173
|
if (prev && prev !== scene.id) issues.push({ code: "duplicate-gameid", message: `${where} address '${sgid}' is already used by scene '${prev}'`, id: scene.id });
|
|
@@ -195,7 +202,7 @@ function validateProject(input) {
|
|
|
195
202
|
if (block.gameId && !isValidGameId(block.gameId)) {
|
|
196
203
|
issues.push({ code: "invalid-gameid", message: `block '${block.id}' address '${block.gameId}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)`, id: block.id });
|
|
197
204
|
}
|
|
198
|
-
const bgid =
|
|
205
|
+
const bgid = addressOf(block);
|
|
199
206
|
if (bgid) {
|
|
200
207
|
const prev = blockGameIds.get(bgid);
|
|
201
208
|
if (prev && prev !== block.id) issues.push({ code: "duplicate-gameid", message: `block '${block.id}' address '${bgid}' is already used by block '${prev}' in ${where}`, id: block.id });
|
|
@@ -251,6 +258,7 @@ function validateProject(input) {
|
|
|
251
258
|
if (p.kind === "line" && p.character && !castNames.has(p.character)) {
|
|
252
259
|
issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
|
|
253
260
|
}
|
|
261
|
+
checkQualifier(p, "prompt");
|
|
254
262
|
}
|
|
255
263
|
}
|
|
256
264
|
if (fallbacks > 1) {
|
|
@@ -280,6 +288,7 @@ function validateProject(input) {
|
|
|
280
288
|
id: beat.id
|
|
281
289
|
});
|
|
282
290
|
}
|
|
291
|
+
checkQualifier(beat, "beat");
|
|
283
292
|
}
|
|
284
293
|
if (node.jump) jumps.push({ to: node.jump.to, from: node.id });
|
|
285
294
|
});
|
|
@@ -352,6 +361,18 @@ function validateProjectFile(project, issues) {
|
|
|
352
361
|
if (project.recordingStatuses) {
|
|
353
362
|
checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
|
|
354
363
|
}
|
|
364
|
+
const seenQualifiers = /* @__PURE__ */ new Set();
|
|
365
|
+
for (const q of project.qualifiers ?? []) {
|
|
366
|
+
if (typeof q.name !== "string" || !q.name.trim()) {
|
|
367
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.gameId)}' has no name` });
|
|
368
|
+
}
|
|
369
|
+
if (typeof q.gameId !== "string" || !isValidGameId(q.gameId)) {
|
|
370
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier '${String(q.name)}' address '${String(q.gameId)}' is invalid (lowercase letters, digits, hyphens; no leading/trailing hyphen)` });
|
|
371
|
+
} else if (seenQualifiers.has(q.gameId)) {
|
|
372
|
+
issues.push({ code: "invalid-qualifier", message: `qualifier address '${q.gameId}' is used more than once` });
|
|
373
|
+
}
|
|
374
|
+
if (typeof q.gameId === "string") seenQualifiers.add(q.gameId);
|
|
375
|
+
}
|
|
355
376
|
for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
|
|
356
377
|
const seen = /* @__PURE__ */ new Set();
|
|
357
378
|
for (const f of fields ?? []) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Patter core: stable id generation, readable-handle computation, canonical (de)serialisation, and the structural validator.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@patterkit/model": "0.
|
|
37
|
+
"@patterkit/model": "0.11.0",
|
|
38
38
|
"@wildwinter/toolkit": "^0.1.0",
|
|
39
39
|
"json5": "^2.2.3"
|
|
40
40
|
}
|