qnce-engine 1.2.3 β†’ 1.3.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.
Files changed (38) hide show
  1. package/README.md +47 -6
  2. package/dist/adapters/contracts.d.ts +23 -0
  3. package/dist/adapters/contracts.d.ts.map +1 -0
  4. package/dist/adapters/contracts.js +5 -0
  5. package/dist/adapters/contracts.js.map +1 -0
  6. package/dist/adapters/story/CustomJSONAdapter.d.ts +10 -0
  7. package/dist/adapters/story/CustomJSONAdapter.d.ts.map +1 -0
  8. package/dist/adapters/story/CustomJSONAdapter.js +94 -0
  9. package/dist/adapters/story/CustomJSONAdapter.js.map +1 -0
  10. package/dist/adapters/story/InkAdapter.d.ts +10 -0
  11. package/dist/adapters/story/InkAdapter.d.ts.map +1 -0
  12. package/dist/adapters/story/InkAdapter.js +44 -0
  13. package/dist/adapters/story/InkAdapter.js.map +1 -0
  14. package/dist/adapters/story/TwisonAdapter.d.ts +10 -0
  15. package/dist/adapters/story/TwisonAdapter.d.ts.map +1 -0
  16. package/dist/adapters/story/TwisonAdapter.js +66 -0
  17. package/dist/adapters/story/TwisonAdapter.js.map +1 -0
  18. package/dist/cli/import.d.ts +3 -0
  19. package/dist/cli/import.d.ts.map +1 -0
  20. package/dist/cli/import.js +166 -0
  21. package/dist/cli/import.js.map +1 -0
  22. package/dist/cli/play.d.ts.map +1 -1
  23. package/dist/cli/play.js +86 -2
  24. package/dist/cli/play.js.map +1 -1
  25. package/dist/engine/core.d.ts +25 -0
  26. package/dist/engine/core.d.ts.map +1 -1
  27. package/dist/engine/core.js +52 -0
  28. package/dist/engine/core.js.map +1 -1
  29. package/dist/persistence/StorageAdapters.d.ts +106 -0
  30. package/dist/persistence/StorageAdapters.d.ts.map +1 -0
  31. package/dist/persistence/StorageAdapters.js +383 -0
  32. package/dist/persistence/StorageAdapters.js.map +1 -0
  33. package/dist/schemas/story-data.schema.json +52 -0
  34. package/dist/schemas/validateStoryData.d.ts +8 -0
  35. package/dist/schemas/validateStoryData.d.ts.map +1 -0
  36. package/dist/schemas/validateStoryData.js +16 -0
  37. package/dist/schemas/validateStoryData.js.map +1 -0
  38. package/package.json +6 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Quantum Narrative Convergence Engine** - A framework-agnostic TypeScript library for creating interactive narrative experiences with quantum-inspired mechanics.
4
4
 
5
- > **πŸš€ Latest v1.2.2:** Complete state persistence, advanced branching with AI integration, autosave & undo/redo system, conditional choice display, and comprehensive UI components with React integration.
5
+ > **πŸš€ Latest v1.3.0 (Import & Persistence):** New `qnce-import` CLI (Custom JSON, Twison with tagsβ†’`meta.tags`, experimental Ink), persistence adapters (Memory, LocalStorage, SessionStorage, File, IndexedDB), and `qnce-play` support for storage backends and non-interactive runs.
6
6
 
7
7
  [![npm version](https://badge.fury.io/js/qnce-engine.svg)](https://badge.fury.io/js/qnce-engine)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
@@ -14,7 +14,7 @@
14
14
  - **Collapse:** Player choices "collapse" the narrative to a specific path, updating state and flags
15
15
  - **Entanglement:** Early decisions affect later outcomes, enabling complex, interconnected stories
16
16
 
17
- ## ✨ Current Features (v1.2.2)
17
+ ## ✨ Current Features (v1.3.0)
18
18
 
19
19
  ### πŸ’Ύ State Persistence & Checkpoints
20
20
  - **Complete save/load system** with data integrity validation
@@ -128,10 +128,6 @@ const complexStory = {
128
128
  {
129
129
  id: 'time-limited-escape',
130
130
  text: 'Escape through the secret passage',
131
- nextNodeId: 'secret-escape',
132
- condition: 'context.timeElapsed < 300 && flags.knowsSecretPath'
133
- },
134
- {
135
131
  id: 'sacrifice-play',
136
132
  text: 'Make the ultimate sacrifice',
137
133
  nextNodeId: 'heroic-end',
@@ -632,6 +628,33 @@ Stories are defined using JSON with the following structure:
632
628
  ```
633
629
 
634
630
  ## CLI Tools
631
+ ### qnce-import (new in v1.3.0)
632
+
633
+ Normalize external story formats into QNCE StoryData with schema + semantic validation.
634
+
635
+ Supported formats:
636
+ - Custom JSON: strict/lenient validation with JSON Schema
637
+ - Twison/Twine JSON: passages, links, robust start detection; tags mapped to `node.meta.tags`
638
+ - Ink JSON: minimal mapping (developer preview). Use `--experimental-ink` for extended best-effort mapping
639
+
640
+ Usage examples:
641
+
642
+ ```bash
643
+ # Autodetect format and write normalized JSON to stdout
644
+ qnce-import path/to/story.json > story.normalized.json
645
+
646
+ # Force a format and fail on schema/semantic issues
647
+ qnce-import --format twison --strict input.json -o normalized.json
648
+
649
+ # Add an ID prefix when merging multiple sources
650
+ qnce-import --id-prefix libA_ a.json > a.norm.json
651
+
652
+ # Read from stdin, write to file
653
+ cat story.json | qnce-import --format custom -o out.json
654
+ ```
655
+
656
+ Exit codes: 0 success, 1 validation failure, 2 unexpected error.
657
+
635
658
 
636
659
  ### qnce-audit
637
660
 
@@ -674,6 +697,24 @@ Features:
674
697
  - State inspection and debugging
675
698
  - Performance monitoring
676
699
  - Session save/load functionality
700
+ - Persistence backends via `--storage` (memory | local | session | file | indexeddb)
701
+ - Non-interactive runs for scripting/CI with JSON summary output
702
+
703
+ Examples:
704
+
705
+ ```bash
706
+ # Basic interactive play
707
+ qnce-play story.json
708
+
709
+ # Use file storage (directory configurable)
710
+ qnce-play story.json --storage file --storage-dir .qnce --save-key session1
711
+
712
+ # Resume a saved session
713
+ qnce-play story.json --storage file --storage-dir .qnce --load-key session1
714
+
715
+ # Scriptable non-interactive run (emits a JSON summary)
716
+ qnce-play story.json --non-interactive --storage memory
717
+ ```
677
718
 
678
719
  ### qnce-perf
679
720
 
@@ -0,0 +1,23 @@
1
+ import type { StoryData } from '../engine/core';
2
+ import type { ValidationResult } from '../engine/validation';
3
+ export interface AdapterOptions {
4
+ namespace?: string;
5
+ strict?: boolean;
6
+ idPrefix?: string;
7
+ }
8
+ export interface StoryAdapter {
9
+ load(source: string | object, options?: AdapterOptions): Promise<StoryData>;
10
+ validate(storyData: StoryData): ValidationResult;
11
+ detect?(source: unknown): boolean;
12
+ mapIds?(storyData: StoryData, strategy?: 'deterministic' | 'passthrough'): StoryData;
13
+ }
14
+ export interface SaveEnvelope<TPayload = any> {
15
+ version: number;
16
+ storyId: string;
17
+ storyVersion: string;
18
+ timestamp: string;
19
+ engineVersion: string;
20
+ checksum: string;
21
+ payload: TPayload;
22
+ }
23
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/adapters/contracts.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5E,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,gBAAgB,CAAC;IACjD,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IAClC,MAAM,CAAC,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,aAAa,GAAG,SAAS,CAAC;CACtF;AAGD,MAAM,WAAW,YAAY,CAAC,QAAQ,GAAG,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,QAAQ,CAAC;CACnB"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ // QNCE Adapter Contracts
3
+ // Shared interfaces for story and storage adapter lanes
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../src/adapters/contracts.ts"],"names":[],"mappings":";AAAA,yBAAyB;AACzB,wDAAwD"}
@@ -0,0 +1,10 @@
1
+ import type { StoryAdapter, AdapterOptions } from '../../adapters/contracts';
2
+ import type { StoryData } from '../../engine/core';
3
+ import type { ValidationResult } from '../../engine/validation';
4
+ export declare class CustomJSONAdapter implements StoryAdapter {
5
+ load(source: string | object, options?: AdapterOptions): Promise<StoryData>;
6
+ validate(_storyData: StoryData): ValidationResult;
7
+ detect(source: unknown): boolean;
8
+ }
9
+ export default CustomJSONAdapter;
10
+ //# sourceMappingURL=CustomJSONAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomJSONAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/story/CustomJSONAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,qBAAa,iBAAkB,YAAW,YAAY;IAC9C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;IA+DjF,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,gBAAgB;IAIjD,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO;CAYjC;AAED,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ // Custom JSON Story Adapter
3
+ // Accepts already-normalized QNCE StoryData or a close variant and ensures it conforms
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.CustomJSONAdapter = void 0;
6
+ class CustomJSONAdapter {
7
+ async load(source, options) {
8
+ const data = typeof source === 'string' ? JSON.parse(source) : source;
9
+ if (!data || typeof data !== 'object')
10
+ throw new Error('Invalid story source');
11
+ const initialNodeId = data.initialNodeId || 'start';
12
+ const nodes = data.nodes || [];
13
+ if (!Array.isArray(nodes))
14
+ throw new Error('Invalid nodes array');
15
+ const normalized = {
16
+ initialNodeId,
17
+ nodes: nodes.map((n) => ({
18
+ id: String(n.id),
19
+ text: String(n.text ?? ''),
20
+ meta: n.meta && typeof n.meta === 'object'
21
+ ? {
22
+ tags: Array.isArray(n.meta.tags) ? n.meta.tags.map((t) => String(t)) : undefined
23
+ }
24
+ : undefined,
25
+ choices: Array.isArray(n.choices)
26
+ ? n.choices.map((c) => ({
27
+ text: String(c.text ?? ''),
28
+ nextNodeId: String(c.nextNodeId ?? ''),
29
+ flagEffects: c.flagEffects,
30
+ flagRequirements: c.flagRequirements,
31
+ timeRequirements: c.timeRequirements,
32
+ inventoryRequirements: c.inventoryRequirements,
33
+ enabled: c.enabled,
34
+ condition: c.condition,
35
+ }))
36
+ : [],
37
+ })),
38
+ };
39
+ if (options?.strict) {
40
+ // Fail on unknown keys at top-level nodes/choices
41
+ const allowedNodeKeys = new Set(['id', 'text', 'choices', 'meta']);
42
+ const allowedChoiceKeys = new Set([
43
+ 'text', 'nextNodeId', 'flagEffects', 'flagRequirements', 'timeRequirements', 'inventoryRequirements', 'enabled', 'condition'
44
+ ]);
45
+ for (const n of nodes) {
46
+ for (const k of Object.keys(n))
47
+ if (!allowedNodeKeys.has(k))
48
+ throw new Error(`Unknown node key: ${k}`);
49
+ for (const c of (n.choices || [])) {
50
+ for (const ck of Object.keys(c))
51
+ if (!allowedChoiceKeys.has(ck))
52
+ throw new Error(`Unknown choice key: ${ck}`);
53
+ }
54
+ }
55
+ }
56
+ else {
57
+ // Lenient mode: warn on unknown keys
58
+ const allowedNodeKeys = new Set(['id', 'text', 'choices', 'meta']);
59
+ const allowedChoiceKeys = new Set([
60
+ 'text', 'nextNodeId', 'flagEffects', 'flagRequirements', 'timeRequirements', 'inventoryRequirements', 'enabled', 'condition'
61
+ ]);
62
+ for (const n of nodes) {
63
+ for (const k of Object.keys(n))
64
+ if (!allowedNodeKeys.has(k))
65
+ console.warn(`[QNCE] Unknown node key ignored: ${k}`);
66
+ for (const c of (n.choices || [])) {
67
+ for (const ck of Object.keys(c))
68
+ if (!allowedChoiceKeys.has(ck))
69
+ console.warn(`[QNCE] Unknown choice key ignored: ${ck}`);
70
+ }
71
+ }
72
+ }
73
+ return normalized;
74
+ }
75
+ validate(_storyData) {
76
+ return { isValid: true };
77
+ }
78
+ detect(source) {
79
+ if (typeof source === 'string') {
80
+ try {
81
+ const obj = JSON.parse(source);
82
+ return !!obj && typeof obj === 'object' && Array.isArray(obj.nodes);
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
88
+ const obj = source;
89
+ return !!obj && typeof obj === 'object' && Array.isArray(obj.nodes);
90
+ }
91
+ }
92
+ exports.CustomJSONAdapter = CustomJSONAdapter;
93
+ exports.default = CustomJSONAdapter;
94
+ //# sourceMappingURL=CustomJSONAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomJSONAdapter.js","sourceRoot":"","sources":["../../../src/adapters/story/CustomJSONAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { StoryAdapter, AdapterOptions } from '../../adapters/contracts';
2
+ import type { StoryData } from '../../engine/core';
3
+ import type { ValidationResult } from '../../engine/validation';
4
+ export declare class InkAdapter implements StoryAdapter {
5
+ load(source: string | object, options?: AdapterOptions): Promise<StoryData>;
6
+ validate(_storyData: StoryData): ValidationResult;
7
+ detect(source: unknown): boolean;
8
+ }
9
+ export default InkAdapter;
10
+ //# sourceMappingURL=InkAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/story/InkAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,qBAAa,UAAW,YAAW,YAAY;IACvC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;IAwBjF,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,gBAAgB;IAEjD,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO;CAKjC;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ // Ink (Inklecate JSON) Story Adapter - minimal stub
3
+ // Many Ink workflows compile to a JSON state machine; here we support a simple node/choice projection
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.InkAdapter = void 0;
6
+ // This is intentionally a very light stub; real Ink JSON can be complex.
7
+ class InkAdapter {
8
+ async load(source, options) {
9
+ const obj = typeof source === 'string' ? JSON.parse(source) : source;
10
+ if (!obj || typeof obj !== 'object')
11
+ throw new Error('Invalid Ink JSON');
12
+ // Heuristic: accept a simplified format { knots: { [name]: { text, choices: [{ text, target }] } } , start?: string }
13
+ const knots = obj.knots || {};
14
+ const idPrefix = options?.idPrefix ?? '';
15
+ if (!options?.experimentalInk && (obj.inkVersion || obj.listDefs || obj.inkState)) {
16
+ console.warn('[QNCE] Ink JSON appears complex; using minimal adapter. Pass experimentalInk to attempt richer import.');
17
+ }
18
+ const nodes = Object.keys(knots).map((k) => ({
19
+ id: `${idPrefix}${k}`,
20
+ text: String(knots[k].text ?? ''),
21
+ choices: Array.isArray(knots[k].choices)
22
+ ? knots[k].choices.map((c) => ({ text: String(c.text ?? ''), nextNodeId: `${idPrefix}${c.target}` }))
23
+ : [],
24
+ }));
25
+ const initialNodeId = `${idPrefix}${(obj.start ?? Object.keys(knots)[0] ?? 'start')}`;
26
+ return { initialNodeId, nodes };
27
+ }
28
+ validate(_storyData) { return { isValid: true }; }
29
+ detect(source) {
30
+ let obj = source;
31
+ if (typeof source === 'string') {
32
+ try {
33
+ obj = JSON.parse(source);
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ return !!obj && typeof obj === 'object' && (!!obj.knots || !!obj.inkVersion);
40
+ }
41
+ }
42
+ exports.InkAdapter = InkAdapter;
43
+ exports.default = InkAdapter;
44
+ //# sourceMappingURL=InkAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InkAdapter.js","sourceRoot":"","sources":["../../../src/adapters/story/InkAdapter.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,sGAAsG;;;AAMtG,yEAAyE;AACzE,MAAa,UAAU;IACrB,KAAK,CAAC,IAAI,CAAC,MAAuB,EAAE,OAAwB;QAC1D,MAAM,GAAG,GAAQ,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1E,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAEzE,sHAAsH;QACtH,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,EAAE,CAAC;QAEzC,IAAI,CAAE,OAAe,EAAE,eAAe,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3F,OAAO,CAAC,IAAI,CAAC,wGAAwG,CAAC,CAAC;QACzH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,GAAG,QAAQ,GAAG,CAAC,EAAE;YACrB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;YACjC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;gBACtC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC1G,CAAC,CAAC,EAAE;SACP,CAAC,CAAC,CAAC;QAEJ,MAAM,aAAa,GAAG,GAAG,QAAQ,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,EAAE,CAAC;QACtF,OAAO,EAAE,aAAa,EAAE,KAAK,EAAe,CAAC;IAC/C,CAAC;IAED,QAAQ,CAAC,UAAqB,IAAsB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAE/E,MAAM,CAAC,MAAe;QACpB,IAAI,GAAG,GAAQ,MAAM,CAAC;QACtB,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAAC,IAAI,CAAC;gBAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,KAAK,CAAC;YAAC,CAAC;QAAC,CAAC;QAC7F,OAAO,CAAC,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/E,CAAC;CACF;AAhCD,gCAgCC;AAED,kBAAe,UAAU,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { StoryAdapter, AdapterOptions } from '../../adapters/contracts';
2
+ import type { StoryData } from '../../engine/core';
3
+ import type { ValidationResult } from '../../engine/validation';
4
+ export declare class TwisonAdapter implements StoryAdapter {
5
+ load(source: string | object, options?: AdapterOptions): Promise<StoryData>;
6
+ validate(_storyData: StoryData): ValidationResult;
7
+ detect(source: unknown): boolean;
8
+ }
9
+ export default TwisonAdapter;
10
+ //# sourceMappingURL=TwisonAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TwisonAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/story/TwisonAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAiBhE,qBAAa,aAAc,YAAW,YAAY;IAC1C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC;IA4CjF,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,gBAAgB;IAIjD,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO;CAOjC;AAED,eAAe,aAAa,CAAC"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ // Twison (Twine JSON) Story Adapter
3
+ // Parses Twison/Twine JSON export into QNCE StoryData
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.TwisonAdapter = void 0;
6
+ class TwisonAdapter {
7
+ async load(source, options) {
8
+ const doc = typeof source === 'string' ? JSON.parse(source) : source;
9
+ if (!doc || !Array.isArray(doc.passages))
10
+ throw new Error('Invalid Twison document');
11
+ const idPrefix = options?.idPrefix ?? '';
12
+ const makeId = (name) => `${idPrefix}${name}`;
13
+ const tagSet = new Set();
14
+ const nodes = doc.passages.map((p) => ({
15
+ id: makeId(p.name),
16
+ text: p.text ?? '',
17
+ choices: (p.links ?? []).map((l) => ({
18
+ text: l.name ?? l.link,
19
+ nextNodeId: makeId(l.link),
20
+ // tags could be surfaced as requirements/effects by a later mapping stage
21
+ })),
22
+ meta: Array.isArray(p.tags) && p.tags.length > 0 ? { tags: p.tags.filter(Boolean).map(String) } : undefined,
23
+ }));
24
+ // Collect tags for visibility (not yet mapped into StoryData schema)
25
+ for (const p of doc.passages) {
26
+ if (Array.isArray(p.tags)) {
27
+ p.tags.filter(Boolean).forEach((t) => tagSet.add(String(t)));
28
+ }
29
+ }
30
+ // Improved start detection:
31
+ // 1) explicit startnode by pid
32
+ // 2) passage named "Start" (case-insensitive)
33
+ // 3) first passage fallback
34
+ let initialNodeId = nodes[0]?.id ?? 'start';
35
+ if (doc.startnode) {
36
+ const startPassage = doc.passages.find((p) => p.pid === doc.startnode) || doc.passages[0];
37
+ if (startPassage)
38
+ initialNodeId = makeId(startPassage.name);
39
+ }
40
+ else {
41
+ const byName = doc.passages.find((p) => /^(start)$/i.test(p.name));
42
+ if (byName)
43
+ initialNodeId = makeId(byName.name);
44
+ }
45
+ // Tags are now captured in node.meta.tags; no warning needed.
46
+ return { initialNodeId, nodes };
47
+ }
48
+ validate(_storyData) {
49
+ return { isValid: true };
50
+ }
51
+ detect(source) {
52
+ let obj = source;
53
+ if (typeof source === 'string') {
54
+ try {
55
+ obj = JSON.parse(source);
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ return !!obj && Array.isArray(obj.passages);
62
+ }
63
+ }
64
+ exports.TwisonAdapter = TwisonAdapter;
65
+ exports.default = TwisonAdapter;
66
+ //# sourceMappingURL=TwisonAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TwisonAdapter.js","sourceRoot":"","sources":["../../../src/adapters/story/TwisonAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/cli/import.ts"],"names":[],"mappings":";AAqKA,OAAO,EAAE,CAAC"}
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const core_js_1 = require("../engine/core.js");
7
+ const CustomJSONAdapter_js_1 = require("../adapters/story/CustomJSONAdapter.js");
8
+ const TwisonAdapter_js_1 = require("../adapters/story/TwisonAdapter.js");
9
+ const InkAdapter_js_1 = require("../adapters/story/InkAdapter.js");
10
+ const validateStoryData_js_1 = require("../schemas/validateStoryData.js");
11
+ /**
12
+ * QNCE Import CLI
13
+ * Detects story format (Custom JSON, Twison, basic Ink) and outputs normalized QNCE StoryData JSON
14
+ */
15
+ function detectAdapter(payload) {
16
+ const candidates = [
17
+ { key: 'json', inst: new CustomJSONAdapter_js_1.CustomJSONAdapter() },
18
+ { key: 'twison', inst: new TwisonAdapter_js_1.TwisonAdapter() },
19
+ { key: 'ink', inst: new InkAdapter_js_1.InkAdapter() }
20
+ ];
21
+ for (const c of candidates) {
22
+ try {
23
+ if (c.inst.detect?.(payload))
24
+ return c;
25
+ }
26
+ catch { }
27
+ }
28
+ return candidates[0];
29
+ }
30
+ async function readStdin() {
31
+ const chunks = [];
32
+ return await new Promise((resolve) => {
33
+ process.stdin.on('data', (d) => chunks.push(Buffer.from(d)));
34
+ process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
35
+ });
36
+ }
37
+ async function main() {
38
+ const args = process.argv.slice(2);
39
+ const showHelp = args.length === 0 && process.stdin.isTTY;
40
+ if (showHelp || args.includes('--help') || args.includes('-h')) {
41
+ console.log(`\nQNCE Import CLI\nUsage: qnce-import <input-file>|(read from stdin) [--out <file>|stdout] [--id-prefix <prefix>] [--format json|twison|ink] [--strict] [--experimental-ink]\n`);
42
+ process.exit(0);
43
+ }
44
+ const formatIdx = args.indexOf('--format');
45
+ const format = formatIdx >= 0 ? args[formatIdx + 1] : undefined;
46
+ const strict = args.includes('--strict');
47
+ const experimentalInk = args.includes('--experimental-ink');
48
+ const idPrefixIndex = args.indexOf('--id-prefix');
49
+ const idPrefix = idPrefixIndex >= 0 ? args[idPrefixIndex + 1] : '';
50
+ const outIndex = args.indexOf('--out');
51
+ const outArg = outIndex >= 0 ? args[outIndex + 1] : undefined;
52
+ let raw;
53
+ let inputName = 'stdin';
54
+ if (args[0] && !args[0].startsWith('--')) {
55
+ const inPath = (0, path_1.resolve)(args[0]);
56
+ inputName = inPath;
57
+ raw = (0, fs_1.readFileSync)(inPath, 'utf-8');
58
+ }
59
+ else {
60
+ raw = await readStdin();
61
+ }
62
+ let exitCode = 0;
63
+ try {
64
+ const json = JSON.parse(raw);
65
+ let selected;
66
+ if (format) {
67
+ const map = {
68
+ json: new CustomJSONAdapter_js_1.CustomJSONAdapter(),
69
+ twison: new TwisonAdapter_js_1.TwisonAdapter(),
70
+ ink: new InkAdapter_js_1.InkAdapter()
71
+ };
72
+ if (!map[format])
73
+ throw new Error(`Unknown format: ${format}`);
74
+ selected = { key: format, inst: map[format] };
75
+ }
76
+ else {
77
+ selected = detectAdapter(json);
78
+ console.log(`ℹ️ Detected format: ${selected.key} (from ${inputName})`);
79
+ }
80
+ const normalized = await selected.inst.load(json, { idPrefix, strict, experimentalInk });
81
+ // Schema validation (strict enforces failure)
82
+ const schema = (0, validateStoryData_js_1.validateStoryData)(normalized);
83
+ if (!schema.valid) {
84
+ const msg = `Schema validation failed with ${schema.errors?.length || 0} error(s).`;
85
+ const fmtErrors = (schema.errors || []).map((e) => ` - ${(e.instancePath ?? e.dataPath) || ''} ${e.message || ''}`).join('\n');
86
+ if (strict) {
87
+ console.error(`❌ ${msg}`);
88
+ if (fmtErrors)
89
+ console.error(fmtErrors);
90
+ process.exit(2);
91
+ }
92
+ else {
93
+ console.warn(`⚠️ ${msg}`);
94
+ if (fmtErrors)
95
+ console.warn(fmtErrors);
96
+ exitCode = Math.max(exitCode, 1);
97
+ }
98
+ }
99
+ // Additional semantic checks: initial node exists, dangling nextNodeId targets
100
+ const nodeIds = new Set(normalized.nodes.map(n => n.id));
101
+ const invalidLinks = [];
102
+ for (const n of normalized.nodes) {
103
+ for (const c of n.choices) {
104
+ if (c.nextNodeId && !nodeIds.has(c.nextNodeId)) {
105
+ invalidLinks.push({ from: n.id, to: c.nextNodeId });
106
+ }
107
+ }
108
+ }
109
+ const initialExists = nodeIds.has(normalized.initialNodeId);
110
+ if (!initialExists) {
111
+ const msg = `Initial node '${normalized.initialNodeId}' does not exist in nodes`;
112
+ if (strict) {
113
+ console.error(`❌ ${msg}`);
114
+ process.exit(2);
115
+ }
116
+ else {
117
+ console.warn(`⚠️ ${msg}`);
118
+ exitCode = Math.max(exitCode, 1);
119
+ }
120
+ }
121
+ if (invalidLinks.length > 0) {
122
+ const msg = `Found ${invalidLinks.length} dangling link(s)`;
123
+ const lines = invalidLinks.slice(0, 10).map(l => ` - ${l.from} -> ${l.to} (missing)`);
124
+ if (strict) {
125
+ console.error(`❌ ${msg}`);
126
+ if (lines.length)
127
+ console.error(lines.join('\n'));
128
+ process.exit(2);
129
+ }
130
+ else {
131
+ console.warn(`⚠️ ${msg}`);
132
+ if (lines.length)
133
+ console.warn(lines.join('\n'));
134
+ exitCode = Math.max(exitCode, 1);
135
+ }
136
+ }
137
+ const story = (0, core_js_1.loadStoryData)(normalized);
138
+ if (outArg && outArg !== 'stdout') {
139
+ const outPath = (0, path_1.resolve)(outArg);
140
+ (0, fs_1.writeFileSync)(outPath, JSON.stringify(story, null, 2));
141
+ console.log(`βœ… Imported and normalized to ${outPath}`);
142
+ }
143
+ else if (!outArg && inputName !== 'stdin') {
144
+ const outPath = (0, path_1.resolve)((0, path_1.basename)(inputName).replace(/\.[^.]+$/, '') + '.qnce.json');
145
+ (0, fs_1.writeFileSync)(outPath, JSON.stringify(story, null, 2));
146
+ console.log(`βœ… Imported and normalized to ${outPath}`);
147
+ }
148
+ else {
149
+ // stdout
150
+ process.stdout.write(JSON.stringify(story, null, 2));
151
+ }
152
+ process.exit(exitCode);
153
+ }
154
+ catch (err) {
155
+ console.error('❌ Import failed:', err?.message || err);
156
+ process.exit(2);
157
+ }
158
+ }
159
+ const isMainModule = require.main === module;
160
+ if (isMainModule) {
161
+ main().catch((e) => {
162
+ console.error('❌ Import failed:', e?.message || e);
163
+ process.exit(2);
164
+ });
165
+ }
166
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/cli/import.ts"],"names":[],"mappings":";;;AAEA,2BAAiD;AACjD,+BAAyC;AAEzC,+CAAkD;AAClD,iFAA2E;AAC3E,yEAAmE;AACnE,mEAA6D;AAC7D,0EAAoE;AAEpE;;;GAGG;AAEH,SAAS,aAAa,CAAC,OAAgB;IACrC,MAAM,UAAU,GAAG;QACjB,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,wCAAiB,EAAE,EAAE;QAC9C,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,gCAAa,EAAE,EAAE;QAC5C,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,0BAAU,EAAE,EAAE;KACvC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC;YAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC;gBAAE,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IAC1D,CAAC;IACD,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,KAAK,UAAU,SAAS;IACtB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACnC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAC1D,IAAI,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACjE,OAAO,CAAC,GAAG,CAAC,gLAAgL,CAAC,CAAC;QAC5L,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACzC,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE9D,IAAI,GAAW,CAAC;IAChB,IAAI,SAAS,GAAG,OAAO,CAAC;IACxB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,IAAA,cAAO,EAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,SAAS,GAAG,MAAM,CAAC;QACnB,GAAG,GAAG,IAAA,iBAAY,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE/B,IAAI,QAAoC,CAAC;QACvC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,GAAwB;gBAC/B,IAAI,EAAE,IAAI,wCAAiB,EAAE;gBAC7B,MAAM,EAAE,IAAI,gCAAa,EAAE;gBAC3B,GAAG,EAAE,IAAI,0BAAU,EAAE;aACtB,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;YAC/D,QAAQ,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,wBAAwB,QAAQ,CAAC,GAAG,UAAU,SAAS,GAAG,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,UAAU,GAAc,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;QAEpG,8CAA8C;QAC9C,MAAM,MAAM,GAAG,IAAA,wCAAiB,EAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,GAAG,GAAG,iCAAiC,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC;YACpF,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpI,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;gBAC1B,IAAI,SAAS;oBAAE,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;gBAC3B,IAAI,SAAS;oBAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACvC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,+EAA+E;QAC/E,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzD,MAAM,YAAY,GAAwC,EAAE,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC1B,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC/C,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;QAC5D,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,iBAAiB,UAAU,CAAC,aAAa,2BAA2B,CAAC;YACjF,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;gBAC3B,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,SAAS,YAAY,CAAC,MAAM,mBAAmB,CAAC;YAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;YACtF,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;gBAC1B,IAAI,KAAK,CAAC,MAAM;oBAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;gBAC3B,IAAI,KAAK,CAAC,MAAM;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACjD,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,IAAA,uBAAa,EAAC,UAAU,CAAC,CAAC;QAExC,IAAI,MAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,IAAA,cAAO,EAAC,MAAM,CAAC,CAAC;YAChC,IAAA,kBAAa,EAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,CAAC,MAAM,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,IAAA,cAAO,EAAC,IAAA,eAAQ,EAAC,SAAS,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC;YACpF,IAAA,kBAAa,EAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,SAAS;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,GAAG,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC;AAC7C,IAAI,YAAY,EAAE,CAAC;IACjB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;QACjB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"play.d.ts","sourceRoot":"","sources":["../../src/cli/play.ts"],"names":[],"mappings":";AAgQA,iBAAS,IAAI,IAAI,IAAI,CA2CpB;AAOD,OAAO,EAAE,IAAI,IAAI,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"play.d.ts","sourceRoot":"","sources":["../../src/cli/play.ts"],"names":[],"mappings":";AAiQA,iBAAS,IAAI,IAAI,IAAI,CAuHpB;AAOD,OAAO,EAAE,IAAI,IAAI,eAAe,EAAE,CAAC"}