@pygmalionjs/pygmalion 0.8.1 → 0.9.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.
Files changed (129) hide show
  1. package/binding-source.d.ts +25 -0
  2. package/dist-lib/{CameraLayer-B2D29VXQ.js → CameraLayer-BdOko8XE.js} +17 -17
  3. package/dist-lib/pygmalion.js +24599 -12746
  4. package/dist-lib/{runtime-BAy8W1mJ.js → runtime-Dxb-IaLN.js} +12414 -11114
  5. package/dist-lib/style.css +1 -1
  6. package/dist-lib/testing.js +10 -10
  7. package/dist-lib/types/binding/catalog.d.ts +10 -0
  8. package/dist-lib/types/binding/contracts.d.ts +202 -0
  9. package/dist-lib/types/binding/documentCommands.d.ts +38 -0
  10. package/dist-lib/types/binding/index.d.ts +6 -0
  11. package/dist-lib/types/binding/preview.d.ts +37 -0
  12. package/dist-lib/types/binding/source.d.ts +109 -0
  13. package/dist-lib/types/binding/useDataProjection.d.ts +9 -0
  14. package/dist-lib/types/binding/validate.d.ts +8 -0
  15. package/dist-lib/types/core/runtime.d.ts +6 -0
  16. package/dist-lib/types/document/appearance.d.ts +112 -0
  17. package/dist-lib/types/document/assets.d.ts +204 -0
  18. package/dist-lib/types/document/components.d.ts +169 -0
  19. package/dist-lib/types/document/contracts.d.ts +171 -0
  20. package/dist-lib/types/document/engine.d.ts +20 -0
  21. package/dist-lib/types/document/exportSettings.d.ts +45 -0
  22. package/dist-lib/types/document/geometry.d.ts +11 -0
  23. package/dist-lib/types/document/layout.d.ts +53 -0
  24. package/dist-lib/types/document/prototype.d.ts +424 -0
  25. package/dist-lib/types/document/prototypeReferences.d.ts +3 -0
  26. package/dist-lib/types/document/richText.d.ts +185 -0
  27. package/dist-lib/types/document/styles.d.ts +216 -0
  28. package/dist-lib/types/document/svgImport.d.ts +17 -0
  29. package/dist-lib/types/document/vector.d.ts +222 -0
  30. package/dist-lib/types/editor/designCompiler.d.ts +7 -0
  31. package/dist-lib/types/editor/designImport.d.ts +7 -0
  32. package/dist-lib/types/editor/inspect.d.ts +8 -0
  33. package/dist-lib/types/editor/revisionCatalogInstall.d.ts +1 -1
  34. package/dist-lib/types/editor/screenInteractions.d.ts +3 -3
  35. package/dist-lib/types/editor/store.d.ts +1 -1
  36. package/dist-lib/types/host/project.d.ts +44 -0
  37. package/dist-lib/types/host/registryConnections.d.ts +11 -0
  38. package/dist-lib/types/host/sourceFilePlan.d.ts +15 -0
  39. package/dist-lib/types/host/sourceWriteback.d.ts +50 -0
  40. package/dist-lib/types/lib.d.ts +101 -3
  41. package/dist-lib/types/token-library/bindings.d.ts +62 -0
  42. package/dist-lib/types/token-library/contracts.d.ts +117 -0
  43. package/dist-lib/types/token-library/engine.d.ts +13 -0
  44. package/dist-lib/types/workspace/WorkspaceShell.d.ts +9 -0
  45. package/dist-lib/types/workspace/application/ApplicationWorkspace.d.ts +3 -0
  46. package/dist-lib/types/workspace/configuration.d.ts +39 -0
  47. package/dist-lib/types/workspace/contracts.d.ts +64 -0
  48. package/dist-lib/types/workspace/controller.d.ts +35 -0
  49. package/dist-lib/types/workspace/documentSessions.d.ts +42 -0
  50. package/dist-lib/types/workspace/edit/AppearancePanel.d.ts +23 -0
  51. package/dist-lib/types/workspace/edit/ArrangeLayers.d.ts +10 -0
  52. package/dist-lib/types/workspace/edit/AssetsPanel.d.ts +16 -0
  53. package/dist-lib/types/workspace/edit/CanvasGuides.d.ts +19 -0
  54. package/dist-lib/types/workspace/edit/ComponentPanel.d.ts +11 -0
  55. package/dist-lib/types/workspace/edit/DataPanel.d.ts +16 -0
  56. package/dist-lib/types/workspace/edit/DesignWorkspace.d.ts +10 -0
  57. package/dist-lib/types/workspace/edit/LayerTree.d.ts +14 -0
  58. package/dist-lib/types/workspace/edit/LayoutPanel.d.ts +13 -0
  59. package/dist-lib/types/workspace/edit/PrototypePanel.d.ts +12 -0
  60. package/dist-lib/types/workspace/edit/ScopeTransferDialog.d.ts +40 -0
  61. package/dist-lib/types/workspace/edit/SelectionHandles.d.ts +52 -0
  62. package/dist-lib/types/workspace/edit/StylesPanel.d.ts +8 -0
  63. package/dist-lib/types/workspace/edit/TextEditor.d.ts +50 -0
  64. package/dist-lib/types/workspace/edit/VectorEditor.d.ts +89 -0
  65. package/dist-lib/types/workspace/edit/VectorToolbar.d.ts +5 -0
  66. package/dist-lib/types/workspace/edit/canvasGuideModel.d.ts +102 -0
  67. package/dist-lib/types/workspace/edit/clipboard.d.ts +35 -0
  68. package/dist-lib/types/workspace/edit/duplicateMetadata.d.ts +2 -0
  69. package/dist-lib/types/workspace/edit/geometryHandles.d.ts +224 -0
  70. package/dist-lib/types/workspace/edit/layerArrangement.d.ts +39 -0
  71. package/dist-lib/types/workspace/edit/layerMoves.d.ts +44 -0
  72. package/dist-lib/types/workspace/edit/projection.d.ts +12 -0
  73. package/dist-lib/types/workspace/edit/scopeTransfer.d.ts +96 -0
  74. package/dist-lib/types/workspace/edit/textStyles.d.ts +75 -0
  75. package/dist-lib/types/workspace/edit/vectorTools.d.ts +136 -0
  76. package/dist-lib/types/workspace/legacyModeAdapter.d.ts +13 -0
  77. package/dist-lib/types/workspace/source/ResponsiveLayoutPanel.d.ts +20 -0
  78. package/dist-lib/types/workspace/source/SourceReview.d.ts +11 -0
  79. package/dist-lib/types/workspace/source/appearanceSource.d.ts +19 -0
  80. package/dist-lib/types/workspace/source/compiler.d.ts +5 -0
  81. package/dist-lib/types/workspace/source/contracts.d.ts +156 -0
  82. package/dist-lib/types/workspace/source/controller.d.ts +50 -0
  83. package/dist-lib/types/workspace/source/documentRevision.d.ts +2 -0
  84. package/dist-lib/types/workspace/source/draftPreviewClient.d.ts +13 -0
  85. package/dist-lib/types/workspace/source/draftRecipe.d.ts +30 -0
  86. package/dist-lib/types/workspace/source/flowContracts.d.ts +72 -0
  87. package/dist-lib/types/workspace/source/prototypeSource.d.ts +4 -0
  88. package/dist-lib/types/workspace/source/responsive.d.ts +13 -0
  89. package/dist-lib/types/workspace/source/responsiveContracts.d.ts +78 -0
  90. package/dist-lib/types/workspace/source/structure.d.ts +21 -0
  91. package/dist-lib/types/workspace/source/textSource.d.ts +28 -0
  92. package/dist-lib/types/workspace/tokens/TokenLibraryContext.d.ts +11 -0
  93. package/dist-lib/types/workspace/tokens/TokenLibraryWorkspace.d.ts +14 -0
  94. package/dist-lib/types/workspace/tokens/contracts.d.ts +74 -0
  95. package/dist-lib/types/workspace/tokens/controller.d.ts +44 -0
  96. package/dist-lib/types/workspace/view/PrototypePlayer.d.ts +17 -0
  97. package/dist-lib/types/workspace/view/StoryboardWorkspace.d.ts +3 -0
  98. package/dist-lib/types/workspace/view/prototypePlayback.d.ts +46 -0
  99. package/draft-preview.d.ts +54 -0
  100. package/inspect.d.ts +2 -0
  101. package/node/design-session.mjs +3 -0
  102. package/node/inspect-plugin.mjs +3 -1
  103. package/node/inspect-writeback.mjs +18 -2
  104. package/node/preview-artifact-store.mjs +10 -7
  105. package/node/source-archive.mjs +88 -0
  106. package/node/source-bindings.mjs +65 -0
  107. package/node/source-file-lock.mjs +15 -0
  108. package/node/source-file-plan.mjs +162 -0
  109. package/node/source-file-proofs.mjs +44 -0
  110. package/node/source-responsive-codec.d.mts +17 -0
  111. package/node/source-responsive-codec.mjs +133 -0
  112. package/node/source-responsive.mjs +189 -0
  113. package/node/source-structure-reconcile.mjs +186 -0
  114. package/node/source-structure-wrapper-codec.d.mts +20 -0
  115. package/node/source-structure-wrapper-codec.mjs +205 -0
  116. package/node/source-structure.mjs +404 -0
  117. package/node/token-source-references.mjs +136 -0
  118. package/node/workspace-draft-preview-plugin.mjs +70 -0
  119. package/node/workspace-draft-preview.mjs +231 -0
  120. package/node/workspace-source-identity.d.mts +1 -0
  121. package/node/workspace-source-identity.mjs +21 -0
  122. package/node/workspace-source-plan.mjs +195 -0
  123. package/package.json +52 -6
  124. package/source-files.d.ts +35 -0
  125. package/source-responsive.d.ts +10 -0
  126. package/source-structure.d.ts +155 -0
  127. package/workspace-source-plan.d.ts +44 -0
  128. package/docs/coverage-contracts.md +0 -444
  129. package/docs/screen-state-contract.md +0 -252
@@ -0,0 +1,195 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { captureSourceFilePlan } from './source-file-plan.mjs';
3
+ import { workspaceSourceFingerprint as fingerprint } from './workspace-source-identity.mjs';
4
+
5
+ const hash = value => createHash('sha256').update(value).digest('hex');
6
+ const record = value => value !== null && typeof value === 'object' && !Array.isArray(value);
7
+ const text = value => typeof value === 'string' && value.trim().length > 0;
8
+ const same = (left, right) => fingerprint(left) === fingerprint(right);
9
+ function freeze(value) {
10
+ if (value && typeof value === 'object') { Object.values(value).forEach(freeze); Object.freeze(value); }
11
+ return value;
12
+ }
13
+ const capture = value => freeze(JSON.parse(fingerprint(value)));
14
+ function limit(value, maximum, label) {
15
+ const selected = value ?? maximum;
16
+ if (!Number.isSafeInteger(selected) || selected < 1 || selected > maximum) throw new Error(`${label} must be a positive integer no greater than ${maximum}.`);
17
+ return selected;
18
+ }
19
+ function targetIdentity(target) {
20
+ if (!record(target) || !text(target.screenId) || !text(target.sourceRevision)
21
+ || Object.keys(target).some(key => !['screenId', 'sourceRevision'].includes(key))) throw new Error('A source target requires an exact screen and source revision.');
22
+ }
23
+ function documentIdentity(document, target) {
24
+ if (!record(document) || document.schemaVersion !== 1 || !text(document.id)
25
+ || document.sourceRevision !== target.sourceRevision || !text(document.rootId)
26
+ || !record(document.nodes) || !record(document.nodes[document.rootId]) || !record(document.metadata)
27
+ || (document.metadata.screenId !== undefined && document.metadata.screenId !== target.screenId)) {
28
+ throw new Error('The complete design document must belong to the selected source target.');
29
+ }
30
+ }
31
+ function baselineIdentity(baseline, target) {
32
+ if (!record(baseline)) throw new Error('A source baseline requires a document and mapping.');
33
+ documentIdentity(baseline.document, target);
34
+ const mapping = baseline.mapping;
35
+ if (!record(mapping) || mapping.version !== 1 || !same(mapping.target, target)
36
+ || mapping.documentId !== baseline.document.id || mapping.baselineFingerprint !== fingerprint(baseline.document)
37
+ || !Array.isArray(mapping.rules) || !Array.isArray(mapping.files) || !mapping.files.length || mapping.files.length > 3000) {
38
+ throw new Error('The complete source mapping must identify its baseline, target, and file proofs.');
39
+ }
40
+ const files = new Set();
41
+ for (const proof of mapping.files) {
42
+ if (!record(proof) || !text(proof.file) || /[\\\x00-\x1f\x7f?#]/.test(proof.file)
43
+ || proof.file.startsWith('/') || /^[a-z]:/i.test(proof.file)
44
+ || proof.file.split('/').some(part => !part || ['.', '..', '.git'].includes(part))
45
+ || (proof.sha256 !== null && (typeof proof.sha256 !== 'string' || !/^[a-f0-9]{64}$/.test(proof.sha256))) || files.has(proof.file)) {
46
+ throw new Error('Source mapping proofs require unique relative files and exact SHA-256 hashes or explicit absence.');
47
+ }
48
+ files.add(proof.file);
49
+ }
50
+ }
51
+ function requestIdentity(target, baseline, document, plan) {
52
+ const parts = { version: 1, target, documentId: document.id, beforeFingerprint: fingerprint(baseline.document),
53
+ afterFingerprint: fingerprint(document), mappingFingerprint: fingerprint(baseline.mapping), payloadFingerprint: fingerprint(plan) };
54
+ return { ...parts, fingerprint: fingerprint(parts) };
55
+ }
56
+ function completePreview(result, plan) {
57
+ const files = new Set(plan.changes.map(change => change.file));
58
+ const complete = entries => Array.isArray(entries) && entries.length === files.size
59
+ && new Set(entries).size === files.size && entries.every(file => files.has(file));
60
+ if (!record(result) || !Array.isArray(result.files) || !complete(result.files.map(entry => entry?.file))
61
+ || !complete(result.affectedFiles) || result.files.some(entry => !text(entry.diff))) {
62
+ throw new Error('Review must include each changed file exactly once with a nonempty diff and affected-file entry.');
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Authorize only plans compiled from a fresh host baseline and completely reviewed for one target.
68
+ * This service performs no source I/O. Host callbacks own compilation semantics and current file proofs.
69
+ */
70
+ export function createWorkspaceSourcePlanService(options) {
71
+ const callbacks = Object.fromEntries(['loadBaseline', 'compilePlan', 'previewPlan', 'applyPlan', 'assertTarget'].map(name => [name, options?.[name]]));
72
+ if (Object.values(callbacks).some(callback => typeof callback !== 'function')) throw new Error('A source plan service requires baseline, compiler, preview, apply, and target callbacks.');
73
+ const ttl = limit(options.receiptTtlMs, 30 * 60_000, 'Receipt lifetime');
74
+ const capacity = limit(options.maxReceipts, 8, 'Receipt capacity');
75
+ const now = options.now ?? Date.now;
76
+ if (typeof now !== 'function') throw new Error('The source receipt clock must be callable.');
77
+ const clock = () => { const value = now(); if (!Number.isFinite(value)) throw new Error('The source receipt clock must return a finite timestamp.'); return value; };
78
+ const receipts = new Map(); const operations = new Set();
79
+ let disposed = false; let busy = null; let revision = 0;
80
+ const keyOf = (target, document) => hash(fingerprint({ target, document }));
81
+ const expired = (receipt, time) => time < receipt.createdAt || time - receipt.createdAt >= ttl;
82
+
83
+ const run = async (exclusive, externalSignal, operation) => {
84
+ if (disposed) throw new Error('The source plan service has been disposed.');
85
+ if (exclusive && busy) throw new Error('Another source plan operation is in progress.');
86
+ externalSignal?.throwIfAborted();
87
+ const controller = new AbortController();
88
+ const current = { controller };
89
+ const failureCleanups = [];
90
+ const abort = () => controller.abort(externalSignal.reason);
91
+ const check = () => {
92
+ controller.signal.throwIfAborted();
93
+ if (disposed) throw new Error('The source plan service has been disposed.');
94
+ };
95
+ operations.add(current); if (exclusive) busy = current;
96
+ try {
97
+ externalSignal?.addEventListener('abort', abort, { once: true });
98
+ const result = await operation({ signal: controller.signal, check, onFailure: cleanup => failureCleanups.push(cleanup) });
99
+ check(); return result;
100
+ } catch (error) {
101
+ for (const cleanup of failureCleanups) cleanup();
102
+ throw error;
103
+ } finally {
104
+ externalSignal?.removeEventListener('abort', abort); operations.delete(current);
105
+ if (busy === current) busy = null;
106
+ }
107
+ };
108
+ const assertTarget = async (target, operation) => {
109
+ operation.check(); targetIdentity(target);
110
+ await callbacks.assertTarget(target, operation.signal); operation.check();
111
+ };
112
+ const currentReceipt = (key, receipt, operation) => {
113
+ operation.check();
114
+ if (!receipt || receipts.get(key) !== receipt || expired(receipt, clock())) {
115
+ receipts.delete(key);
116
+ throw new Error('The compiled source receipt is missing, expired, or consumed. Compile and review again.');
117
+ }
118
+ };
119
+ const validate = async (input, operation, requireReview) => {
120
+ const request = capture(input);
121
+ if (!record(request) || request.sourceKind !== 'file-plan') throw new Error('A compiled file-plan request is required.');
122
+ targetIdentity(request.target); documentIdentity(request.document, request.target);
123
+ await assertTarget(request.target, operation);
124
+ const key = keyOf(request.target, request.document); const receipt = receipts.get(key);
125
+ currentReceipt(key, receipt, operation);
126
+ const identity = requestIdentity(receipt.target, receipt.baseline, receipt.document, receipt.plan);
127
+ if (!same(request.payload, receipt.plan) || !same(request.identity, identity)) throw new Error('The request must match the complete compiled document, source mapping, target, and file plan.');
128
+ if (requireReview && receipt.reviewed !== identity.fingerprint) throw new Error('Review the complete source diff for this target before applying or building.');
129
+ return { request, key, receipt };
130
+ };
131
+ return {
132
+ load(input, signal) {
133
+ return run(false, signal, async operation => {
134
+ const target = capture(input); await assertTarget(target, operation);
135
+ const baseline = capture(await callbacks.loadBaseline(target, operation.signal));
136
+ operation.check(); baselineIdentity(baseline, target); operation.check(); return baseline;
137
+ });
138
+ },
139
+ compile(input, signal) {
140
+ return run(true, signal, async operation => {
141
+ const { target, baseline, document } = capture(input);
142
+ targetIdentity(target); baselineIdentity(baseline, target); documentIdentity(document, target);
143
+ if (document.id !== baseline.document.id || document.rootId !== baseline.document.rootId) throw new Error('The edited document must retain the source baseline identity.');
144
+ if (same(document, baseline.document)) throw new Error('There are no document changes to compile.');
145
+ const key = keyOf(target, document); receipts.delete(key);
146
+ await assertTarget(target, operation);
147
+ const actual = capture(await callbacks.loadBaseline(target, operation.signal));
148
+ operation.check(); baselineIdentity(actual, target);
149
+ if (!same(baseline, actual)) throw new Error('The supplied baseline differs from the current host source baseline.');
150
+ const compiled = await callbacks.compilePlan(capture({ target, baseline: actual, document }), operation.signal);
151
+ operation.check();
152
+ const plan = capture(captureSourceFilePlan(compiled, { sourceRevision: target.sourceRevision, allowedFiles: actual.mapping.files.map(proof => proof.file) }));
153
+ if (!same(plan.proofs, actual.mapping.files)) throw new Error('The compiled plan must retain every source mapping proof exactly.');
154
+ if (plan.changes.some(change => change.beforeSha256 === hash(change.content))) throw new Error('Every compiled source change must produce different content.');
155
+ const createdAt = clock(); operation.check();
156
+ for (const [oldKey, receipt] of receipts) if (expired(receipt, createdAt)) receipts.delete(oldKey);
157
+ const receipt = { target, baseline: actual, document, plan, createdAt, reviewed: null };
158
+ receipts.set(key, receipt);
159
+ operation.onFailure(() => { if (receipts.get(key) === receipt) receipts.delete(key); });
160
+ while (receipts.size > capacity) receipts.delete(receipts.keys().next().value);
161
+ return plan;
162
+ });
163
+ },
164
+ preview(input, signal) {
165
+ return run(true, signal, async operation => {
166
+ const { request, key, receipt } = await validate(input, operation, false);
167
+ receipt.reviewed = null;
168
+ const result = capture(await callbacks.previewPlan(receipt.plan, request.target, operation.signal));
169
+ currentReceipt(key, receipt, operation); completePreview(result, receipt.plan);
170
+ receipt.reviewed = request.identity.fingerprint;
171
+ operation.onFailure(() => { receipt.reviewed = null; });
172
+ return capture({ ...result, revision: ++revision });
173
+ });
174
+ },
175
+ validatePreviewRequest(input, signal) {
176
+ return run(true, signal, async operation => (await validate(input, operation, true)).request);
177
+ },
178
+ apply(input, signal) {
179
+ return run(true, signal, async operation => {
180
+ const { request, receipt } = await validate(input, operation, true);
181
+ // Once a writer starts, every cached review requires recompilation, including failed or aborted writes.
182
+ receipts.clear(); operation.check();
183
+ const result = capture(await callbacks.applyPlan(receipt.plan, request.target, operation.signal));
184
+ operation.check();
185
+ if (!record(result) || !Number.isSafeInteger(result.applied) || result.applied < 1) throw new Error('The source writer did not record the reviewed changes. Compile and review again.');
186
+ return result;
187
+ });
188
+ },
189
+ dispose() {
190
+ if (disposed) return;
191
+ disposed = true; receipts.clear();
192
+ for (const operation of operations) operation.controller.abort(new Error('The source plan service has been disposed.'));
193
+ },
194
+ };
195
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -48,12 +48,34 @@
48
48
  "types": "./dist-lib/types/testing.d.ts",
49
49
  "default": "./dist-lib/testing.js"
50
50
  },
51
- "./style.css": "./dist-lib/style.css"
51
+ "./style.css": "./dist-lib/style.css",
52
+ "./binding-source": {
53
+ "types": "./binding-source.d.ts",
54
+ "default": "./node/source-bindings.mjs"
55
+ },
56
+ "./draft-preview": {
57
+ "types": "./draft-preview.d.ts",
58
+ "default": "./node/workspace-draft-preview.mjs"
59
+ },
60
+ "./source-files": {
61
+ "types": "./source-files.d.ts",
62
+ "default": "./node/source-file-plan.mjs"
63
+ },
64
+ "./source-structure": {
65
+ "types": "./source-structure.d.ts",
66
+ "default": "./node/source-structure.mjs"
67
+ },
68
+ "./source-plan": {
69
+ "types": "./workspace-source-plan.d.ts",
70
+ "default": "./node/workspace-source-plan.mjs"
71
+ },
72
+ "./source-responsive": {
73
+ "types": "./source-responsive.d.ts",
74
+ "default": "./node/source-responsive.mjs"
75
+ }
52
76
  },
53
77
  "files": [
54
78
  "dist-lib",
55
- "docs/coverage-contracts.md",
56
- "docs/screen-state-contract.md",
57
79
  "node/contract.mjs",
58
80
  "node/contract.d.mts",
59
81
  "node/component-branches.mjs",
@@ -79,6 +101,12 @@
79
101
  "node/source-diff.mjs",
80
102
  "node/source-graph.mjs",
81
103
  "node/source-operations.mjs",
104
+ "node/source-file-proofs.mjs",
105
+ "node/workspace-source-identity.mjs",
106
+ "node/workspace-source-identity.d.mts",
107
+ "node/workspace-draft-preview-plugin.mjs",
108
+ "node/workspace-draft-preview.mjs",
109
+ "draft-preview.d.ts",
82
110
  "node/storyboard-canonical.mjs",
83
111
  "node/storyboard-capture-scheduler.mjs",
84
112
  "node/storyboard-capture-sessions.mjs",
@@ -92,13 +120,31 @@
92
120
  "qa.d.ts",
93
121
  "storyboard.d.ts",
94
122
  "runtime.d.ts",
95
- "vite.d.ts"
123
+ "vite.d.ts",
124
+ "node/source-bindings.mjs",
125
+ "binding-source.d.ts",
126
+ "node/source-file-plan.mjs",
127
+ "node/source-file-lock.mjs",
128
+ "source-files.d.ts",
129
+ "node/source-archive.mjs",
130
+ "node/token-source-references.mjs",
131
+ "node/source-structure.mjs",
132
+ "source-structure.d.ts",
133
+ "node/workspace-source-plan.mjs",
134
+ "workspace-source-plan.d.ts",
135
+ "node/source-structure-reconcile.mjs",
136
+ "node/source-structure-wrapper-codec.mjs",
137
+ "node/source-structure-wrapper-codec.d.mts",
138
+ "node/source-responsive.mjs",
139
+ "source-responsive.d.ts",
140
+ "node/source-responsive-codec.mjs",
141
+ "node/source-responsive-codec.d.mts"
96
142
  ],
97
143
  "scripts": {
98
144
  "dev": "vite",
99
145
  "build": "tsc && vite build",
100
146
  "build:lib": "vite build --config vite.lib.config.ts && npm run build:types",
101
- "test": "node --test node/*.test.mjs src/*.test.mjs src/**/*.test.mjs",
147
+ "test": "node --test 'node/**/*.test.mjs' 'src/**/*.test.mjs'",
102
148
  "lint": "eslint .",
103
149
  "lint:language": "node scripts/lint-language.mjs",
104
150
  "typecheck": "tsc --noEmit",
@@ -0,0 +1,35 @@
1
+ import type { SourceFilePlan } from './dist-lib/types/host/sourceFilePlan.js';
2
+ import type { InspectPreviewResult } from './dist-lib/types/editor/host.js';
3
+
4
+ export interface SourceFilePlanOptions {
5
+ sourceRevision: string;
6
+ allowedFiles: readonly string[];
7
+ excludedSourcePrefixes?: readonly string[];
8
+ signal?: AbortSignal;
9
+ }
10
+ /** Capture and validate a serializable plan without reading or changing source files. */
11
+ export declare function captureSourceFilePlan(plan: SourceFilePlan, options: SourceFilePlanOptions): SourceFilePlan;
12
+ export declare function previewSourceFilePlan(root: string, plan: SourceFilePlan, options: SourceFilePlanOptions): Promise<Pick<InspectPreviewResult, 'files' | 'affectedFiles'>>;
13
+ export declare function applySourceFilePlan(root: string, plan: SourceFilePlan, options: SourceFilePlanOptions): Promise<{ applied: number; changed: number; files: string[] }>;
14
+
15
+ export interface TokenSourceReferenceEdit { start: number; end: number; before: string; after: string }
16
+ export interface TokenSourceReferenceDiagnostic { code: string; message: string; file: string; start?: number; name?: string }
17
+ /** Diagnostics block the complete rewrite; offsets address UTF-16 source positions. */
18
+ export declare function rewriteTokenSourceReferences(input: {
19
+ file: string;
20
+ source: string;
21
+ renames: readonly { from: string; to: string | null }[];
22
+ }): {
23
+ source: string;
24
+ edits: TokenSourceReferenceEdit[];
25
+ diagnostics: TokenSourceReferenceDiagnostic[];
26
+ references: { name: string; start: number; end: number; kind: 'static' | 'finite' }[];
27
+ };
28
+
29
+ /**
30
+ * Build/validation workers must settle before the callback returns and its archive is removed.
31
+ * Cancellation waits for callback settlement and cleanup, then rejects even if the callback returned.
32
+ */
33
+ export declare function withSourceArchive<T>(options: {
34
+ projectRoot: string; sourceRevision: string; appDirectory?: string; signal?: AbortSignal;
35
+ }, operation: (context: Readonly<{ appRoot: string; sourceRevision: string; signal: AbortSignal }>) => Promise<T>): Promise<T>;
@@ -0,0 +1,10 @@
1
+ import type { InspectApplyPayload } from './dist-lib/types/editor/inspect.js';
2
+ import type { SourceFilePlan } from './dist-lib/types/host/sourceFilePlan.js';
3
+ import type { ResponsiveLayoutDocument, ResponsiveLayoutRange, ResponsiveLayoutSourceTarget, WorkspaceResponsiveSourceMapping } from './dist-lib/types/workspace/source/responsiveContracts.js';
4
+ export interface ResponsiveSourceInputFile { file: string; source: string; sha256: string }
5
+ export declare function readResponsiveLayoutSource(input: { files: readonly ResponsiveSourceInputFile[]; ranges: readonly ResponsiveLayoutRange[]; targets: readonly ResponsiveLayoutSourceTarget[] }): ResponsiveLayoutDocument;
6
+ export declare function compileResponsiveSourcePlan(input: {
7
+ appRoot: string; sourceRevision: string; mapping: WorkspaceResponsiveSourceMapping;
8
+ before: ResponsiveLayoutDocument; after: ResponsiveLayoutDocument;
9
+ additionalStyles?: InspectApplyPayload['styles']; signal?: AbortSignal;
10
+ }): Promise<SourceFilePlan>;
@@ -0,0 +1,155 @@
1
+ import type { SourceFilePlan } from './dist-lib/types/host/sourceFilePlan.js';
2
+ import type { WorkspaceStructureGroupLayout, WorkspaceStructureWrapperDeclaration } from './dist-lib/types/workspace/source/contracts.js';
3
+
4
+ export interface JsxStructureAnchor { nodeId: string; file: string; start: number; end: number; expected: string }
5
+ export type JsxStructureGroupLayout = WorkspaceStructureGroupLayout;
6
+ /**
7
+ * Host declaration for element wrappers. A group with a layout compiles to
8
+ * `<tagName className={styleAlias.<derived class>}>` plus one managed block in the
9
+ * stylesheet; spacing pairs document pixel values with the host's exact CSS spellings.
10
+ */
11
+ export type JsxStructureWrapperContract = WorkspaceStructureWrapperDeclaration;
12
+ /** The contract and the complete stylesheet bytes matching the JSX source being read. */
13
+ export interface JsxStructureWrapperEvidence { contract: JsxStructureWrapperContract; source: string }
14
+ export type JsxStructureOperation =
15
+ | { kind: 'reorder'; parentId: string; children: readonly string[] }
16
+ /** Index addresses the final list after removing the node from its original parent. */
17
+ | { kind: 'reparent'; nodeId: string; parentId: string; index: number }
18
+ /** A layout requires the plan's wrapper contract; without one the group is an anonymous fragment. */
19
+ | { kind: 'group'; nodeIds: readonly string[]; groupId: string; layout?: JsxStructureGroupLayout }
20
+ /** Removes an anonymous fragment or a generated wrapper element together with its managed block. */
21
+ | { kind: 'ungroup'; nodeId: string };
22
+ export interface JsxStructureDiagnostic { code: string; message: string; file?: string; nodeId?: string }
23
+ export interface JsxStructureSourceNode {
24
+ nodeId: string; parentId: string | null; children: string[];
25
+ kind: 'element' | 'expression' | 'fragment' | 'text';
26
+ tagName?: string;
27
+ /** Only a static string literal is reported; bindings and CSS module expressions are not evaluated. */
28
+ className?: string;
29
+ /** Present only for a generated wrapper element proved against its managed stylesheet block. */
30
+ layout?: JsxStructureGroupLayout;
31
+ wrapper?: { className: string };
32
+ runtimePath: number[]; start: number; end: number;
33
+ }
34
+ /** Persist alongside the source in the same reviewed file plan; offsets are UTF-16 source positions. */
35
+ export interface JsxStructureIdentity {
36
+ version: 1;
37
+ file: string;
38
+ sourceSha256: string;
39
+ rootId: string;
40
+ opaqueNodeIds: string[];
41
+ anchors: { nodeId: string; start: number; end: number }[];
42
+ /** Generated wrapper groups; absent when the island has none. */
43
+ wrappers?: { nodeId: string; className: string }[];
44
+ }
45
+ /** Checks the supplied anchor snippets and full AST projection before returning serializable evidence. */
46
+ export declare function createJsxStructureIdentity(input: {
47
+ file: string; source: string; rootId: string; anchors: readonly JsxStructureAnchor[]; opaqueNodeIds?: readonly string[];
48
+ /** Required to record wrapper groups; the stylesheet must be the bytes reviewed with this source. */
49
+ wrapper?: JsxStructureWrapperEvidence;
50
+ }): JsxStructureIdentity;
51
+ /**
52
+ * Throws on stale bytes, malformed identity, missing nodes or ambiguous ranges. Never guesses IDs.
53
+ * Recorded wrappers require the wrapper evidence; a wrapper whose element or managed block was
54
+ * edited by hand fails closed instead of degrading to a plain element.
55
+ */
56
+ export declare function reimportJsxStructureSource(input: {
57
+ file: string; source: string; identity: JsxStructureIdentity; wrapper?: JsxStructureWrapperEvidence;
58
+ }): { anchors: JsxStructureAnchor[]; nodes: JsxStructureSourceNode[] };
59
+ /**
60
+ * Throws for invalid source, ambiguous roots, or opaque offsets outside the projected tree.
61
+ * Runtime paths exclude blank multiline indentation and comment-only expressions. Expressions
62
+ * that evaluate to false or undefined still occupy one slot; their values are never evaluated.
63
+ */
64
+ export declare function inspectJsxStructureSource(input: {
65
+ file: string; source: string; rootStart: number; opaqueNodeStarts?: readonly number[];
66
+ }): { anchors: JsxStructureAnchor[]; nodes: JsxStructureSourceNode[] };
67
+ /**
68
+ * Pure source compilation; the host audits allowed containers and the complete canonical-document delta.
69
+ * Expressions move only as complete opaque chunks within one static JSX tree. Rendered text and
70
+ * standalone comments in an edited parent block ambiguous hierarchy changes. Groups without a layout
71
+ * are anonymous fragments, which do not provide a DOM box. With a wrapper contract, a group with a
72
+ * layout emits one intrinsic element and a managed block in the contract stylesheet, which must be
73
+ * supplied in `files` and allowed for writing. Neither form certifies preservation of component
74
+ * reconciliation state across a React remount. Props, handlers, and expression source are preserved
75
+ * without evaluation or fixture substitution.
76
+ */
77
+ export declare function compileJsxStructurePlan(input: {
78
+ sourceRevision: string;
79
+ files: readonly { file: string; source: string; sha256: string }[];
80
+ anchors: readonly JsxStructureAnchor[];
81
+ operations: readonly JsxStructureOperation[];
82
+ allowedFiles: readonly string[];
83
+ excludedSourcePrefixes?: readonly string[];
84
+ wrapper?: JsxStructureWrapperContract;
85
+ }): { status: 'ready'; payload: SourceFilePlan; anchors: JsxStructureAnchor[] }
86
+ | { status: 'unchanged'; anchors: JsxStructureAnchor[] }
87
+ | { status: 'blocked'; diagnostics: JsxStructureDiagnostic[] };
88
+
89
+ export interface JsxStructureReconciliationInput {
90
+ file: string;
91
+ baseline: { sourceRevision: string; source: string; identity: JsxStructureIdentity };
92
+ external: {
93
+ /** May equal the baseline commit for explicitly checked working-copy changes. */
94
+ sourceRevision: string;
95
+ source: string;
96
+ sha256: string;
97
+ /**
98
+ * Complete host-supplied correspondence to the current AST. Retained IDs explicitly mean
99
+ * the same source-owned nodes; new IDs must be fresh. Core never infers this mapping from
100
+ * names, positions, snippets, runtime values, or example data. Every range and hash is checked.
101
+ */
102
+ identity: JsxStructureIdentity;
103
+ };
104
+ /** Complete audited draft structure relative to baseline; the host checks other document deltas. */
105
+ operations: readonly JsxStructureOperation[];
106
+ allowedFiles: readonly string[];
107
+ excludedSourcePrefixes?: readonly string[];
108
+ /** Required when either identity records wrapper groups or the draft adds one. */
109
+ wrapper?: {
110
+ contract: JsxStructureWrapperContract;
111
+ baselineSource: string;
112
+ externalSource: string;
113
+ externalSha256: string;
114
+ };
115
+ }
116
+ export interface JsxStructureReconciliationShape {
117
+ kind: JsxStructureSourceNode['kind'];
118
+ parentId: string | null;
119
+ children: string[];
120
+ layout: JsxStructureGroupLayout | null;
121
+ }
122
+ export interface JsxStructureReconciliationDiagnostic extends JsxStructureDiagnostic {
123
+ action: 'refresh-source' | 'map-identities' | 'resolve-overlap';
124
+ baseline?: JsxStructureReconciliationShape | null;
125
+ external?: JsxStructureReconciliationShape | null;
126
+ draft?: JsxStructureReconciliationShape | null;
127
+ }
128
+ export interface ReconciledJsxStructureSource {
129
+ file: string;
130
+ baselineSourceRevision: string;
131
+ baselineSourceSha256: string;
132
+ /** The external revision is retained; the baseline is never silently relabeled. */
133
+ sourceRevision: string;
134
+ externalSourceSha256: string;
135
+ source: string;
136
+ /** Persist with the resulting source using a separately reviewed, exact file plan. */
137
+ identity: JsxStructureIdentity;
138
+ /** The contract stylesheet after the merge; present only when wrapper input was supplied. */
139
+ stylesheetSource?: string;
140
+ anchors: JsxStructureAnchor[];
141
+ nodes: JsxStructureSourceNode[];
142
+ diagnostics: readonly [];
143
+ }
144
+ export type JsxStructureReconciliationResult =
145
+ | (ReconciledJsxStructureSource & { status: 'ready'; payload: SourceFilePlan })
146
+ | (ReconciledJsxStructureSource & { status: 'unchanged' })
147
+ | { status: 'conflict'; diagnostics: readonly JsxStructureReconciliationDiagnostic[] };
148
+ /**
149
+ * Pure, explicit three-way reconciliation. Disjoint child-list changes merge; overlapping lists,
150
+ * removals, incompatible boundaries, competing wrapper layouts and ambiguous identities require
151
+ * explicit conflict resolution. External props and expression bytes survive structure-only edits.
152
+ * Host source ownership and document/binding validation still apply. A ready plan proves the
153
+ * latest external bytes only.
154
+ */
155
+ export declare function reconcileJsxStructurePlan(input: JsxStructureReconciliationInput): JsxStructureReconciliationResult;
@@ -0,0 +1,44 @@
1
+ import type { DesignDocument } from './dist-lib/types/document/contracts.js';
2
+ import type { SourceFilePlan } from './dist-lib/types/host/sourceFilePlan.js';
3
+ import type { InspectApplyResult, InspectPreviewResult } from './dist-lib/types/editor/host.js';
4
+ import type { WorkspaceTarget } from './dist-lib/types/workspace/contracts.js';
5
+ import type { WorkspaceFileSourceRequest, WorkspaceSourceBaseline } from './dist-lib/types/workspace/source/flowContracts.js';
6
+
7
+ export interface WorkspaceSourcePlanCompileInput {
8
+ target: WorkspaceTarget;
9
+ baseline: WorkspaceSourceBaseline;
10
+ document: DesignDocument;
11
+ }
12
+
13
+ export interface WorkspaceSourcePlanServiceOptions {
14
+ /** Callbacks receive detached, deeply frozen JSON captures and must settle on cancellation. */
15
+ loadBaseline(target: WorkspaceTarget, signal: AbortSignal): Promise<WorkspaceSourceBaseline>;
16
+ /** Owns full document validation and the source semantics of every changed field. */
17
+ compilePlan(input: WorkspaceSourcePlanCompileInput, signal: AbortSignal): Promise<SourceFilePlan>;
18
+ /** Must check current file proofs; the service checks complete, nonempty diff coverage. */
19
+ previewPlan(plan: SourceFilePlan, target: WorkspaceTarget, signal: AbortSignal): Promise<Pick<InspectPreviewResult, 'files' | 'affectedFiles'> & Partial<Pick<InspectPreviewResult, 'qa'>>>;
20
+ /** Must recheck current source revision and proofs and apply the complete plan atomically. */
21
+ applyPlan(plan: SourceFilePlan, target: WorkspaceTarget, signal: AbortSignal): Promise<InspectApplyResult>;
22
+ assertTarget(target: WorkspaceTarget, signal: AbortSignal): void | Promise<void>;
23
+ /** Default and maximum: 30 minutes. Review never extends this lifetime. */
24
+ receiptTtlMs?: number;
25
+ /** Default and maximum: 8 compiled document receipts. */
26
+ maxReceipts?: number;
27
+ now?: () => number;
28
+ }
29
+
30
+ export interface WorkspaceSourcePlanService {
31
+ /** Loads may run concurrently; every other operation is mutually exclusive. */
32
+ load(target: WorkspaceTarget, signal?: AbortSignal): Promise<WorkspaceSourceBaseline>;
33
+ /** Reloads and compares the complete baseline before issuing a bounded server receipt. */
34
+ compile(input: WorkspaceSourcePlanCompileInput, signal?: AbortSignal): Promise<SourceFilePlan>;
35
+ preview(request: WorkspaceFileSourceRequest, signal?: AbortSignal): Promise<InspectPreviewResult>;
36
+ /** A writer attempt consumes all receipts. Cancellation does not establish rollback. */
37
+ apply(request: WorkspaceFileSourceRequest, signal?: AbortSignal): Promise<InspectApplyResult>;
38
+ /** Required before preparing an immutable built artifact from a browser-supplied file plan. */
39
+ validatePreviewRequest(request: WorkspaceFileSourceRequest, signal?: AbortSignal): Promise<WorkspaceFileSourceRequest>;
40
+ /** Aborts in-flight callbacks, clears receipts, and rejects their later completions. */
41
+ dispose(): void;
42
+ }
43
+
44
+ export declare function createWorkspaceSourcePlanService(options: WorkspaceSourcePlanServiceOptions): WorkspaceSourcePlanService;