@patterkit/core 0.4.1 → 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 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)) {
@@ -290,6 +296,7 @@ function validateProject(input) {
290
296
  if (p.kind === "line" && p.character && !castNames.has(p.character)) {
291
297
  issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
292
298
  }
299
+ checkQualifier(p, "prompt");
293
300
  }
294
301
  }
295
302
  if (fallbacks > 1) {
@@ -319,6 +326,7 @@ function validateProject(input) {
319
326
  id: beat.id
320
327
  });
321
328
  }
329
+ checkQualifier(beat, "beat");
322
330
  }
323
331
  if (node.jump) jumps.push({ to: node.jump.to, from: node.id });
324
332
  });
@@ -391,6 +399,18 @@ function validateProjectFile(project, issues) {
391
399
  if (project.recordingStatuses) {
392
400
  checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
393
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
+ }
394
414
  for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
395
415
  const seen = /* @__PURE__ */ new Set();
396
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)) {
@@ -252,6 +258,7 @@ function validateProject(input) {
252
258
  if (p.kind === "line" && p.character && !castNames.has(p.character)) {
253
259
  issues.push({ code: "unknown-character", message: `prompt '${p.id}' speaker '${p.character}' is not in the project cast`, id: p.id });
254
260
  }
261
+ checkQualifier(p, "prompt");
255
262
  }
256
263
  }
257
264
  if (fallbacks > 1) {
@@ -281,6 +288,7 @@ function validateProject(input) {
281
288
  id: beat.id
282
289
  });
283
290
  }
291
+ checkQualifier(beat, "beat");
284
292
  }
285
293
  if (node.jump) jumps.push({ to: node.jump.to, from: node.id });
286
294
  });
@@ -353,6 +361,18 @@ function validateProjectFile(project, issues) {
353
361
  if (project.recordingStatuses) {
354
362
  checkLadderNames(project.recordingStatuses.map((s) => s.name), "recordingStatuses", issues);
355
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
+ }
356
376
  for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
357
377
  const seen = /* @__PURE__ */ new Set();
358
378
  for (const f of fields ?? []) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/core",
3
- "version": "0.4.1",
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.10.0",
37
+ "@patterkit/model": "0.11.0",
38
38
  "@wildwinter/toolkit": "^0.1.0",
39
39
  "json5": "^2.2.3"
40
40
  }