@patterkit/core 0.6.2 → 0.7.1
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 +46 -13
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +47 -14
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -96,6 +96,7 @@ ${indent}}`;
|
|
|
96
96
|
var import_model = require("@patterkit/model");
|
|
97
97
|
var import_model2 = require("@patterkit/model");
|
|
98
98
|
var import_model3 = require("@patterkit/model");
|
|
99
|
+
var import_expr = require("@wildwinter/expr");
|
|
99
100
|
function choiceLoopsBack(choice, ...targets) {
|
|
100
101
|
const back = new Set(targets.filter((s) => typeof s === "string" && s.length > 0));
|
|
101
102
|
const hits = (n) => n.type === "snippet" ? !!n.jump && back.has(n.jump.to) : (n.children ?? []).some(hits);
|
|
@@ -388,6 +389,20 @@ function validateProject(input) {
|
|
|
388
389
|
});
|
|
389
390
|
}
|
|
390
391
|
}
|
|
392
|
+
const labelled = (project.cast ?? []).filter((c) => c.qualifierLabels && typeof c.qualifierLabels === "object");
|
|
393
|
+
const usedPairs = labelled.length === 0 ? /* @__PURE__ */ new Set() : new Set((0, import_model.usedSpeakerPairs)(scenes.filter((sc) => Array.isArray(sc?.blocks) && sc.blocks.every((b) => Array.isArray(b?.children)))).map((p) => `${p.character}\0${p.qualifier}`));
|
|
394
|
+
for (const c of labelled) {
|
|
395
|
+
const labels = c.qualifierLabels;
|
|
396
|
+
if (!labels || typeof labels !== "object") continue;
|
|
397
|
+
for (const q of Object.keys(labels)) {
|
|
398
|
+
if (usedPairs.has(`${c.name}\0${q}`)) continue;
|
|
399
|
+
issues.push({
|
|
400
|
+
code: "unused-speaker-label",
|
|
401
|
+
severity: "warning",
|
|
402
|
+
message: `'${c.name}' has its own label with the qualifier '${q}', but no line has that speaker with that qualifier`
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
391
406
|
return issues;
|
|
392
407
|
}
|
|
393
408
|
function isQualifierEntry(q) {
|
|
@@ -468,6 +483,21 @@ function validateProjectFile(project, issues) {
|
|
|
468
483
|
}
|
|
469
484
|
if (typeof q.gameId === "string") seenQualifiers.add(q.gameId);
|
|
470
485
|
}
|
|
486
|
+
const pattern = project.speakerPattern;
|
|
487
|
+
if (pattern !== void 0 && typeof pattern !== "string") {
|
|
488
|
+
issues.push({ code: "invalid-speaker-label", message: "the speaker pattern must be text" });
|
|
489
|
+
}
|
|
490
|
+
for (const c of project.cast ?? []) {
|
|
491
|
+
const labels = c.qualifierLabels;
|
|
492
|
+
if (labels === void 0) continue;
|
|
493
|
+
if (!labels || typeof labels !== "object" || Array.isArray(labels)) {
|
|
494
|
+
issues.push({ code: "invalid-speaker-label", message: `'${c.name}' qualifier labels must map a qualifier to its label` });
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
for (const [q, label] of Object.entries(labels)) {
|
|
498
|
+
if (typeof label !== "string") issues.push({ code: "invalid-speaker-label", message: `'${c.name}' label with the qualifier '${q}' must be text` });
|
|
499
|
+
}
|
|
500
|
+
}
|
|
471
501
|
for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
|
|
472
502
|
const seen = /* @__PURE__ */ new Set();
|
|
473
503
|
for (const f of fields ?? []) {
|
|
@@ -572,18 +602,21 @@ function checkDecls(decls, where, issues) {
|
|
|
572
602
|
});
|
|
573
603
|
}
|
|
574
604
|
if (decl.type === "quality") {
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
605
|
+
for (const problem2 of (0, import_expr.qualityDeclarationProblems)(decl.stages)) {
|
|
606
|
+
switch (problem2.kind) {
|
|
607
|
+
case "too-few-stages":
|
|
608
|
+
issues.push({
|
|
609
|
+
code: "invalid-declaration",
|
|
610
|
+
message: `${where}: '${decl.name}' is a quality but declares ${problem2.count === 0 ? "no stages" : "only one stage"} (a ladder needs at least two)`
|
|
611
|
+
});
|
|
612
|
+
break;
|
|
613
|
+
case "empty-stage":
|
|
614
|
+
issues.push({ code: "invalid-declaration", message: `${where}: '${decl.name}' has an empty stage name` });
|
|
615
|
+
break;
|
|
616
|
+
case "duplicate-stage":
|
|
617
|
+
issues.push({ code: "invalid-declaration", message: `${where}: '${decl.name}' declares stage '${problem2.stage}' more than once` });
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
587
620
|
}
|
|
588
621
|
}
|
|
589
622
|
if (decl.default !== void 0 && !defaultMatchesType(decl.default, decl)) {
|
|
@@ -607,7 +640,7 @@ function defaultMatchesType(value, decl) {
|
|
|
607
640
|
case "flags":
|
|
608
641
|
return Array.isArray(value) && value.every((v) => typeof v === "string" && (decl.values?.includes(v) ?? true));
|
|
609
642
|
case "quality":
|
|
610
|
-
return typeof value === "string" && (decl.stages
|
|
643
|
+
return typeof value === "string" && !(0, import_expr.qualityDeclarationProblems)(decl.stages, value).some((p) => p.kind === "default-not-a-stage");
|
|
611
644
|
}
|
|
612
645
|
}
|
|
613
646
|
|
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" | "unknown-qualifier" | "invalid-qualifier" | "invalid-pad" | "pad-overlaps-seam" | "prompt-pad-without-beat" | "qualifier-without-speaker" | "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-pad" | "pad-overlaps-seam" | "prompt-pad-without-beat" | "qualifier-without-speaker" | "invalid-speaker-label" | "unused-speaker-label" | "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" | "unknown-qualifier" | "invalid-qualifier" | "invalid-pad" | "pad-overlaps-seam" | "prompt-pad-without-beat" | "qualifier-without-speaker" | "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-pad" | "pad-overlaps-seam" | "prompt-pad-without-beat" | "qualifier-without-speaker" | "invalid-speaker-label" | "unused-speaker-label" | "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,9 +55,10 @@ ${indent}}`;
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// src/validate.ts
|
|
58
|
-
import { walkNodes, DEFAULT_WRITING_STATUSES, DEFAULT_RECORDING_STATUSES, DEFAULT_DOCUMENTATION_CLASSES, projectQualifiers, PAD_AFTER_MIN, PAD_AFTER_MAX } from "@patterkit/model";
|
|
58
|
+
import { walkNodes, DEFAULT_WRITING_STATUSES, DEFAULT_RECORDING_STATUSES, DEFAULT_DOCUMENTATION_CLASSES, projectQualifiers, usedSpeakerPairs, PAD_AFTER_MIN, PAD_AFTER_MAX } from "@patterkit/model";
|
|
59
59
|
import { isValidGameId, effectiveGameId } from "@patterkit/model";
|
|
60
60
|
import { isValidPropertyName, propertyNameify, RESERVED_PROPERTY_NAMES } from "@patterkit/model";
|
|
61
|
+
import { qualityDeclarationProblems } from "@wildwinter/expr";
|
|
61
62
|
function choiceLoopsBack(choice, ...targets) {
|
|
62
63
|
const back = new Set(targets.filter((s) => typeof s === "string" && s.length > 0));
|
|
63
64
|
const hits = (n) => n.type === "snippet" ? !!n.jump && back.has(n.jump.to) : (n.children ?? []).some(hits);
|
|
@@ -350,6 +351,20 @@ function validateProject(input) {
|
|
|
350
351
|
});
|
|
351
352
|
}
|
|
352
353
|
}
|
|
354
|
+
const labelled = (project.cast ?? []).filter((c) => c.qualifierLabels && typeof c.qualifierLabels === "object");
|
|
355
|
+
const usedPairs = labelled.length === 0 ? /* @__PURE__ */ new Set() : new Set(usedSpeakerPairs(scenes.filter((sc) => Array.isArray(sc?.blocks) && sc.blocks.every((b) => Array.isArray(b?.children)))).map((p) => `${p.character}\0${p.qualifier}`));
|
|
356
|
+
for (const c of labelled) {
|
|
357
|
+
const labels = c.qualifierLabels;
|
|
358
|
+
if (!labels || typeof labels !== "object") continue;
|
|
359
|
+
for (const q of Object.keys(labels)) {
|
|
360
|
+
if (usedPairs.has(`${c.name}\0${q}`)) continue;
|
|
361
|
+
issues.push({
|
|
362
|
+
code: "unused-speaker-label",
|
|
363
|
+
severity: "warning",
|
|
364
|
+
message: `'${c.name}' has its own label with the qualifier '${q}', but no line has that speaker with that qualifier`
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
353
368
|
return issues;
|
|
354
369
|
}
|
|
355
370
|
function isQualifierEntry(q) {
|
|
@@ -430,6 +445,21 @@ function validateProjectFile(project, issues) {
|
|
|
430
445
|
}
|
|
431
446
|
if (typeof q.gameId === "string") seenQualifiers.add(q.gameId);
|
|
432
447
|
}
|
|
448
|
+
const pattern = project.speakerPattern;
|
|
449
|
+
if (pattern !== void 0 && typeof pattern !== "string") {
|
|
450
|
+
issues.push({ code: "invalid-speaker-label", message: "the speaker pattern must be text" });
|
|
451
|
+
}
|
|
452
|
+
for (const c of project.cast ?? []) {
|
|
453
|
+
const labels = c.qualifierLabels;
|
|
454
|
+
if (labels === void 0) continue;
|
|
455
|
+
if (!labels || typeof labels !== "object" || Array.isArray(labels)) {
|
|
456
|
+
issues.push({ code: "invalid-speaker-label", message: `'${c.name}' qualifier labels must map a qualifier to its label` });
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
for (const [q, label] of Object.entries(labels)) {
|
|
460
|
+
if (typeof label !== "string") issues.push({ code: "invalid-speaker-label", message: `'${c.name}' label with the qualifier '${q}' must be text` });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
433
463
|
for (const [kind, fields] of Object.entries(project.gameDataFields ?? {})) {
|
|
434
464
|
const seen = /* @__PURE__ */ new Set();
|
|
435
465
|
for (const f of fields ?? []) {
|
|
@@ -534,18 +564,21 @@ function checkDecls(decls, where, issues) {
|
|
|
534
564
|
});
|
|
535
565
|
}
|
|
536
566
|
if (decl.type === "quality") {
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
567
|
+
for (const problem2 of qualityDeclarationProblems(decl.stages)) {
|
|
568
|
+
switch (problem2.kind) {
|
|
569
|
+
case "too-few-stages":
|
|
570
|
+
issues.push({
|
|
571
|
+
code: "invalid-declaration",
|
|
572
|
+
message: `${where}: '${decl.name}' is a quality but declares ${problem2.count === 0 ? "no stages" : "only one stage"} (a ladder needs at least two)`
|
|
573
|
+
});
|
|
574
|
+
break;
|
|
575
|
+
case "empty-stage":
|
|
576
|
+
issues.push({ code: "invalid-declaration", message: `${where}: '${decl.name}' has an empty stage name` });
|
|
577
|
+
break;
|
|
578
|
+
case "duplicate-stage":
|
|
579
|
+
issues.push({ code: "invalid-declaration", message: `${where}: '${decl.name}' declares stage '${problem2.stage}' more than once` });
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
549
582
|
}
|
|
550
583
|
}
|
|
551
584
|
if (decl.default !== void 0 && !defaultMatchesType(decl.default, decl)) {
|
|
@@ -569,7 +602,7 @@ function defaultMatchesType(value, decl) {
|
|
|
569
602
|
case "flags":
|
|
570
603
|
return Array.isArray(value) && value.every((v) => typeof v === "string" && (decl.values?.includes(v) ?? true));
|
|
571
604
|
case "quality":
|
|
572
|
-
return typeof value === "string" && (decl.stages
|
|
605
|
+
return typeof value === "string" && !qualityDeclarationProblems(decl.stages, value).some((p) => p.kind === "default-not-a-stage");
|
|
573
606
|
}
|
|
574
607
|
}
|
|
575
608
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patterkit/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
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,8 @@
|
|
|
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.14.0",
|
|
38
|
+
"@wildwinter/expr": "^0.5.2",
|
|
38
39
|
"@wildwinter/toolkit": "^0.1.0",
|
|
39
40
|
"json5": "^2.2.3"
|
|
40
41
|
}
|