circuitjson-toolkit 1.0.17 → 1.1.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/NOTICE.md +31 -0
- package/README.md +267 -107
- package/docs/api.md +501 -96
- package/docs/capabilities.md +70 -0
- package/docs/migration/behaviors.md +45 -0
- package/docs/migration/parser.md +60 -0
- package/docs/migration/renderers.md +515 -0
- package/docs/migration/root.md +740 -0
- package/docs/migration.md +120 -0
- package/docs/model-format.md +175 -57
- package/docs/provenance.md +206 -0
- package/docs/release-notes-v1.1.0.md +154 -0
- package/docs/testing.md +117 -7
- package/package.json +31 -5
- package/spec/api-baseline-v1.0.17.json +1 -0
- package/spec/baseline-provenance-v1.0.17.json +7 -0
- package/spec/circuitjson-schema-snapshot.json +321 -0
- package/spec/circuitjson-schema-source.json +28 -0
- package/spec/feature-preservation.json +1 -0
- package/spec/library-scope.md +27 -20
- package/src/capabilities.mjs +1 -0
- package/src/core/ArchiveEntryPath.mjs +93 -0
- package/src/core/ArchiveLimits.mjs +31 -0
- package/src/core/ArchiveLimitsValidator.mjs +107 -0
- package/src/core/AsyncInputOwnership.mjs +56 -0
- package/src/core/AttachedValueLimits.mjs +67 -0
- package/src/core/CircuitJsonDiagnosticIndexer.mjs +184 -0
- package/src/core/CircuitJsonDocument.mjs +19 -61
- package/src/core/CircuitJsonElementTypes.mjs +10 -0
- package/src/core/CircuitJsonElementValidator.mjs +98 -847
- package/src/core/CircuitJsonIndexer.mjs +274 -194
- package/src/core/CircuitJsonManufacturingBuilder.mjs +167 -164
- package/src/core/CircuitJsonParser.mjs +75 -13
- package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +12 -6
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +108 -10
- package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +1 -1
- package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +44 -38
- package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +146 -28
- package/src/core/CircuitJsonPcbPrimitiveFields.mjs +70 -4
- package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +18 -2
- package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +26 -9
- package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +7 -6
- package/src/core/CircuitJsonSerializedInputAudit.mjs +87 -0
- package/src/core/CircuitJsonSourceMetadata.mjs +5 -1
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +3 -1
- package/src/core/CircuitJsonToolkitElementSchema.mjs +218 -0
- package/src/core/CircuitJsonUnitParsers.mjs +101 -0
- package/src/core/CircuitJsonUnits.mjs +13 -87
- package/src/core/CircuitJsonUpstreamSchema.mjs +9 -0
- package/src/core/CircuitJsonUpstreamValidator.mjs +418 -0
- package/src/core/CircuitJsonValidationUnits.mjs +6 -0
- package/src/core/ManufacturingService.mjs +323 -0
- package/src/core/Parser.mjs +343 -0
- package/src/core/ParserOptions.mjs +333 -0
- package/src/core/PcbBoundsSelectionModel.mjs +55 -19
- package/src/core/PcbDiagnosticFocusModel.mjs +42 -11
- package/src/core/PcbInteractionIndex.mjs +368 -0
- package/src/core/PcbInteractionPrimitiveModel.mjs +393 -62
- package/src/core/ProjectAsyncInputOwner.mjs +70 -0
- package/src/core/ProjectLoader.mjs +975 -0
- package/src/core/SimulationService.mjs +790 -0
- package/src/core/ToolkitCapabilities.mjs +130 -0
- package/src/core/ZipArchiveInspector.mjs +649 -0
- package/src/core/context/BinaryDataSnapshot.mjs +217 -0
- package/src/core/context/CircuitJsonContextIndexes.mjs +96 -0
- package/src/core/context/CircuitJsonDerivedCache.mjs +114 -0
- package/src/core/context/CircuitJsonDocumentContext.mjs +353 -0
- package/src/core/context/CircuitJsonLegacyModel.mjs +147 -0
- package/src/core/context/CircuitJsonLegacyNormalizer.mjs +847 -0
- package/src/core/context/CircuitJsonMetadataBoundary.mjs +76 -0
- package/src/core/context/CircuitJsonModelFreezeTraversal.mjs +179 -0
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +920 -0
- package/src/core/context/CircuitJsonSchematicTableNormalizer.mjs +314 -0
- package/src/core/context/CircuitJsonValidationAuthority.mjs +39 -0
- package/src/core/context/CircuitJsonValidationProof.mjs +217 -0
- package/src/core/context/PcbPrimitivePreparation.mjs +198 -0
- package/src/core/context/PcbSpatialIndex.mjs +701 -0
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +128 -0
- package/src/core/context/StructuredDataSnapshot.mjs +683 -0
- package/src/core/contracts/DocumentResult.mjs +198 -0
- package/src/core/contracts/ProjectResult.mjs +96 -0
- package/src/core/contracts/RuntimeProxyBoundary.mjs +48 -0
- package/src/core/contracts/ToolkitAsset.mjs +493 -0
- package/src/core/contracts/ToolkitDiagnostic.mjs +38 -0
- package/src/core/contracts/ToolkitError.mjs +176 -0
- package/src/core/contracts/ToolkitProgress.mjs +89 -0
- package/src/core/interaction/CanonicalInteractionOptions.mjs +246 -0
- package/src/core/interaction/PcbInteractionBounds.mjs +167 -0
- package/src/core/query/CircuitTraversal.mjs +343 -0
- package/src/core/query/ComponentGrouping.mjs +275 -0
- package/src/core/query/QueryNetlistBuilder.mjs +306 -0
- package/src/core/query/QueryService.mjs +435 -0
- package/src/core/query/RegexPattern.mjs +75 -0
- package/src/core/rendering/CanonicalBomOrder.mjs +81 -0
- package/src/core/rendering/CanonicalBomRows.mjs +92 -0
- package/src/core/rendering/CanonicalRenderOptions.mjs +498 -0
- package/src/core/rendering/CanonicalSvgDocument.mjs +102 -0
- package/src/core/rendering/PcbRenderPlan.mjs +429 -0
- package/src/core/rendering/SchematicSheetSelector.mjs +335 -0
- package/src/core/scene3d/PcbScene3dBuilder.mjs +906 -0
- package/src/core/scene3d/PcbScene3dPreparator.mjs +47 -0
- package/src/core/scene3d/Scene3dAssetIndex.mjs +284 -0
- package/src/core/scene3d/Scene3dBoardModel.mjs +596 -0
- package/src/core/scene3d/Scene3dDocumentMetadata.mjs +167 -0
- package/src/core/scene3d/Scene3dFreeze.mjs +37 -0
- package/src/core/scene3d/Scene3dIdRegistry.mjs +34 -0
- package/src/core/scene3d/Scene3dInputPreflight.mjs +193 -0
- package/src/core/scene3d/Scene3dMaterials.mjs +58 -0
- package/src/core/scene3d/Scene3dModelReference.mjs +134 -0
- package/src/core/scene3d/Scene3dOptions.mjs +363 -0
- package/src/core/scene3d/SceneAssetResolver.mjs +441 -0
- package/src/core/simulation/SimulationParameterCloner.mjs +543 -0
- package/src/core/worker/ParserWorkerClient.mjs +997 -0
- package/src/core/worker/ToolkitWorkerProtocol.mjs +412 -0
- package/src/core/worker/WorkerRequestData.mjs +623 -0
- package/src/core/worker/WorkerResponseData.mjs +905 -0
- package/src/extensions.mjs +37 -0
- package/src/index.mjs +14 -9
- package/src/interaction.mjs +2 -0
- package/src/manufacturing.mjs +1 -0
- package/src/parser.mjs +12 -2
- package/src/project.mjs +5 -0
- package/src/query.mjs +1 -0
- package/src/renderers.mjs +3 -29
- package/src/scene3d.mjs +3 -0
- package/src/simulation.mjs +1 -0
- package/src/styles/renderers.css +24 -0
- package/src/testing/ToolkitContractFixtures.mjs +124 -0
- package/src/testing/ToolkitLoopbackWorker.mjs +174 -0
- package/src/testing/runToolkitContract.mjs +705 -0
- package/src/testing.mjs +3 -0
- package/src/ui/BomTableRenderer.mjs +304 -0
- package/src/ui/CircuitJsonPcbBoardSvgRenderer.mjs +80 -0
- package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +3 -5
- package/src/ui/CircuitJsonPcbSvgRenderer.mjs +63 -43
- package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +3 -5
- package/src/ui/CircuitJsonSchematicDebugRenderer.mjs +164 -0
- package/src/ui/CircuitJsonSchematicImageSvgRenderer.mjs +210 -0
- package/src/ui/CircuitJsonSchematicLineRenderer.mjs +86 -0
- package/src/ui/CircuitJsonSchematicSheetSymbolSvgRenderer.mjs +98 -0
- package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +117 -17
- package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +67 -20
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +45 -9
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +151 -148
- package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +4 -292
- package/src/ui/PcbSvgRenderer.mjs +41 -0
- package/src/ui/SafeSvgPaint.mjs +26 -0
- package/src/ui/SafeXmlText.mjs +60 -0
- package/src/ui/SchematicGeometryBounds.mjs +540 -0
- package/src/ui/SchematicSvgRenderer.mjs +110 -0
- package/src/ui/SchematicTableGeometry.mjs +319 -0
- package/src/ui/SchematicTextAnchor.mjs +55 -0
- package/src/ui/SchematicTextBounds.mjs +98 -0
- package/src/workers/parser.worker.mjs +59 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { CircuitJsonValidationProof } from '../context/CircuitJsonValidationProof.mjs'
|
|
2
|
+
import { CircuitJsonReadOnlyDocument } from '../context/CircuitJsonReadOnlyDocument.mjs'
|
|
3
|
+
import { CircuitJsonSerializedInputAudit } from '../CircuitJsonSerializedInputAudit.mjs'
|
|
4
|
+
import { ToolkitAsset } from './ToolkitAsset.mjs'
|
|
5
|
+
import { ToolkitDiagnostic, cloneSafeValue } from './ToolkitDiagnostic.mjs'
|
|
6
|
+
|
|
7
|
+
const DOCUMENT_SCHEMA = 'ecad-toolkit.document.v1'
|
|
8
|
+
const EXTENSION_SCHEMA = 'ecad-toolkit.extension.v1'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds canonical document result envelopes.
|
|
12
|
+
*/
|
|
13
|
+
export class DocumentResult {
|
|
14
|
+
/**
|
|
15
|
+
* Creates one canonical document envelope without validating its model.
|
|
16
|
+
* @param {Record<string, any>} [fields] Document fields.
|
|
17
|
+
* @returns {Record<string, any>} Canonical document result.
|
|
18
|
+
*/
|
|
19
|
+
static create(fields = {}) {
|
|
20
|
+
return DocumentResult.#create(fields, false)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates the common envelope with the requested extension ownership mode.
|
|
25
|
+
* @param {Record<string, any>} fields Document fields.
|
|
26
|
+
* @param {boolean} readonlyExtensions Whether to capture immutable extensions now.
|
|
27
|
+
* @returns {Record<string, any>} Canonical document result.
|
|
28
|
+
*/
|
|
29
|
+
static #create(fields, readonlyExtensions) {
|
|
30
|
+
const source = DocumentResult.#source(fields)
|
|
31
|
+
const audit = CircuitJsonSerializedInputAudit.inspect(
|
|
32
|
+
fields.model,
|
|
33
|
+
source.fileName
|
|
34
|
+
)
|
|
35
|
+
return {
|
|
36
|
+
schema: DOCUMENT_SCHEMA,
|
|
37
|
+
id: String(
|
|
38
|
+
fields.id || DocumentResult.sourceId('document', source)
|
|
39
|
+
),
|
|
40
|
+
modelSchema: {
|
|
41
|
+
name: 'circuit-json',
|
|
42
|
+
version: '0.0.446'
|
|
43
|
+
},
|
|
44
|
+
model: Array.isArray(fields.model) ? fields.model : [],
|
|
45
|
+
source,
|
|
46
|
+
extensions: DocumentResult.extensionForSource(
|
|
47
|
+
source.format,
|
|
48
|
+
fields.extensions,
|
|
49
|
+
{ readonly: readonlyExtensions }
|
|
50
|
+
),
|
|
51
|
+
assets: Array.isArray(fields.assets)
|
|
52
|
+
? fields.assets.map((asset) =>
|
|
53
|
+
CircuitJsonReadOnlyDocument.protectOwnedAsset(
|
|
54
|
+
ToolkitAsset.create(asset)
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
: [],
|
|
58
|
+
diagnostics: [
|
|
59
|
+
...(Array.isArray(fields.diagnostics)
|
|
60
|
+
? fields.diagnostics
|
|
61
|
+
: []),
|
|
62
|
+
...audit.diagnostics
|
|
63
|
+
].map((diagnostic) => ToolkitDiagnostic.create(diagnostic)),
|
|
64
|
+
statistics: {
|
|
65
|
+
...cloneSafeValue(fields.statistics, {}),
|
|
66
|
+
...audit.statistics
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates a canonical envelope whose model is validated and immutable.
|
|
73
|
+
* @param {Record<string, any>} [fields] Document fields.
|
|
74
|
+
* @param {{ sourceReference?: object }} [runtime] Runtime-only fields.
|
|
75
|
+
* @returns {Record<string, any>} Proven canonical document result.
|
|
76
|
+
*/
|
|
77
|
+
static createValidated(fields = {}, runtime = {}) {
|
|
78
|
+
const document = DocumentResult.#create(fields, true)
|
|
79
|
+
if (Object.hasOwn(runtime || {}, 'sourceReference')) {
|
|
80
|
+
Object.defineProperty(document, 'sourceReference', {
|
|
81
|
+
configurable: false,
|
|
82
|
+
enumerable: false,
|
|
83
|
+
value: runtime.sourceReference,
|
|
84
|
+
writable: false
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
return CircuitJsonValidationProof.validateAndAttach(document)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates a stable id from normalized source identity only.
|
|
92
|
+
* @param {string} prefix Id prefix.
|
|
93
|
+
* @param {Record<string, any>} source Normalized source.
|
|
94
|
+
* @returns {string} Stable source id.
|
|
95
|
+
*/
|
|
96
|
+
static sourceId(prefix, source) {
|
|
97
|
+
const identity = JSON.stringify({
|
|
98
|
+
format: source.format,
|
|
99
|
+
fileName: source.fileName || '',
|
|
100
|
+
entryNames: Array.isArray(source.entryNames)
|
|
101
|
+
? [...source.entryNames].sort()
|
|
102
|
+
: []
|
|
103
|
+
})
|
|
104
|
+
let hash = 2166136261
|
|
105
|
+
for (const character of identity) {
|
|
106
|
+
hash ^= character.codePointAt(0)
|
|
107
|
+
hash = Math.imul(hash, 16777619)
|
|
108
|
+
}
|
|
109
|
+
return `${prefix}-${(hash >>> 0).toString(16).padStart(8, '0')}`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Selects and normalizes the extension namespace owned by a source.
|
|
114
|
+
* @param {string} format Source format.
|
|
115
|
+
* @param {unknown} extensions Extension candidates.
|
|
116
|
+
* @param {{ readonly?: boolean }} [options] Extension ownership options.
|
|
117
|
+
* @returns {Record<string, any>} Normalized extension map.
|
|
118
|
+
*/
|
|
119
|
+
static extensionForSource(format, extensions, options = {}) {
|
|
120
|
+
if (format === 'circuitjson') return {}
|
|
121
|
+
const candidate = extensions?.[format]
|
|
122
|
+
const hasCandidate = candidate && typeof candidate === 'object'
|
|
123
|
+
if (!hasCandidate) return {}
|
|
124
|
+
if (options.readonly === true && hasCandidate) {
|
|
125
|
+
return CircuitJsonReadOnlyDocument.copyReadonlyExtensionValue(
|
|
126
|
+
candidate,
|
|
127
|
+
(snapshot) =>
|
|
128
|
+
DocumentResult.#normalizedExtension(format, snapshot)
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
const cloned = hasCandidate ? cloneSafeValue(candidate, {}) : {}
|
|
132
|
+
return DocumentResult.#normalizedExtension(format, cloned)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Normalizes one already-owned source namespace without another deep copy.
|
|
137
|
+
* @param {string} format Source format.
|
|
138
|
+
* @param {Record<string, any>} namespace Owned mutable namespace.
|
|
139
|
+
* @returns {Record<string, any>} Normalized extension map.
|
|
140
|
+
*/
|
|
141
|
+
static #normalizedExtension(format, namespace) {
|
|
142
|
+
const metadata = namespace.$meta || {}
|
|
143
|
+
const fields = { ...namespace }
|
|
144
|
+
delete fields.$meta
|
|
145
|
+
return {
|
|
146
|
+
[format]: {
|
|
147
|
+
$meta: {
|
|
148
|
+
schema: String(metadata.schema || EXTENSION_SCHEMA),
|
|
149
|
+
completeness: String(metadata.completeness || 'canonical'),
|
|
150
|
+
included: Array.isArray(metadata.included)
|
|
151
|
+
? metadata.included.map(String)
|
|
152
|
+
: [],
|
|
153
|
+
omitted: Array.isArray(metadata.omitted)
|
|
154
|
+
? metadata.omitted.map(String)
|
|
155
|
+
: []
|
|
156
|
+
},
|
|
157
|
+
...fields
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Normalizes document source identity and type.
|
|
164
|
+
* @param {Record<string, any>} fields Document fields.
|
|
165
|
+
* @returns {Record<string, any>} Normalized source.
|
|
166
|
+
*/
|
|
167
|
+
static #source(fields) {
|
|
168
|
+
const provided = cloneSafeValue(fields.source, {})
|
|
169
|
+
const fileName = DocumentResult.#fileName(
|
|
170
|
+
provided.fileName || fields.fileName
|
|
171
|
+
)
|
|
172
|
+
const format = String(
|
|
173
|
+
provided.format || fields.format || 'circuitjson'
|
|
174
|
+
).toLowerCase()
|
|
175
|
+
const suffix = fileName.split('.').pop()
|
|
176
|
+
return {
|
|
177
|
+
...provided,
|
|
178
|
+
format,
|
|
179
|
+
fileName,
|
|
180
|
+
fileType: String(
|
|
181
|
+
provided.fileType ||
|
|
182
|
+
fields.fileType ||
|
|
183
|
+
(suffix && suffix !== fileName ? suffix : 'json')
|
|
184
|
+
).toLowerCase()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Normalizes path separators without resolving filesystem paths.
|
|
190
|
+
* @param {unknown} fileName File name candidate.
|
|
191
|
+
* @returns {string} Normalized source file name.
|
|
192
|
+
*/
|
|
193
|
+
static #fileName(fileName) {
|
|
194
|
+
return String(fileName || '')
|
|
195
|
+
.replaceAll('\\', '/')
|
|
196
|
+
.replace(/^\.\//u, '')
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { DocumentResult } from './DocumentResult.mjs'
|
|
2
|
+
import { ToolkitAsset } from './ToolkitAsset.mjs'
|
|
3
|
+
import { ToolkitDiagnostic, cloneSafeValue } from './ToolkitDiagnostic.mjs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds canonical multi-document project envelopes.
|
|
7
|
+
*/
|
|
8
|
+
export class ProjectResult {
|
|
9
|
+
/**
|
|
10
|
+
* Creates one canonical project result.
|
|
11
|
+
* @param {Record<string, any>} [fields] Project fields.
|
|
12
|
+
* @returns {Record<string, any>} Canonical project result.
|
|
13
|
+
*/
|
|
14
|
+
static create(fields = {}) {
|
|
15
|
+
const documents = Array.isArray(fields.documents)
|
|
16
|
+
? fields.documents
|
|
17
|
+
: []
|
|
18
|
+
const source = ProjectResult.#source(fields.source, documents)
|
|
19
|
+
const documentIds = documents.map((document) => String(document.id))
|
|
20
|
+
const id = String(
|
|
21
|
+
fields.id || DocumentResult.sourceId('project', source)
|
|
22
|
+
)
|
|
23
|
+
return {
|
|
24
|
+
schema: 'ecad-toolkit.project.v1',
|
|
25
|
+
id,
|
|
26
|
+
source,
|
|
27
|
+
documents,
|
|
28
|
+
project:
|
|
29
|
+
fields.project == null
|
|
30
|
+
? null
|
|
31
|
+
: ProjectResult.#project(
|
|
32
|
+
fields.project,
|
|
33
|
+
id,
|
|
34
|
+
source,
|
|
35
|
+
documentIds
|
|
36
|
+
),
|
|
37
|
+
extensions: DocumentResult.extensionForSource(
|
|
38
|
+
source.format,
|
|
39
|
+
fields.extensions
|
|
40
|
+
),
|
|
41
|
+
assets: Array.isArray(fields.assets)
|
|
42
|
+
? fields.assets.map((asset) => ToolkitAsset.create(asset))
|
|
43
|
+
: [],
|
|
44
|
+
diagnostics: Array.isArray(fields.diagnostics)
|
|
45
|
+
? fields.diagnostics.map((diagnostic) =>
|
|
46
|
+
ToolkitDiagnostic.create(diagnostic)
|
|
47
|
+
)
|
|
48
|
+
: [],
|
|
49
|
+
statistics: cloneSafeValue(fields.statistics, {})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalizes a present project descriptor to the exact common shape.
|
|
55
|
+
* @param {unknown} project Project candidate.
|
|
56
|
+
* @param {string} resultId Project-result id.
|
|
57
|
+
* @param {Record<string, any>} source Normalized project source.
|
|
58
|
+
* @param {string[]} documentIds Loaded document ids.
|
|
59
|
+
* @returns {{ id: string, name: string, format: string, documentIds: string[], relationships: object[] }} Canonical project descriptor.
|
|
60
|
+
*/
|
|
61
|
+
static #project(project, resultId, source, documentIds) {
|
|
62
|
+
const provided = cloneSafeValue(project, {})
|
|
63
|
+
return {
|
|
64
|
+
id: String(provided.id || `${resultId}-project`),
|
|
65
|
+
name: String(provided.name || ''),
|
|
66
|
+
format: String(provided.format || source.format),
|
|
67
|
+
documentIds,
|
|
68
|
+
relationships: Array.isArray(provided.relationships)
|
|
69
|
+
? provided.relationships
|
|
70
|
+
: []
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Normalizes project source identity.
|
|
76
|
+
* @param {unknown} source Source candidate.
|
|
77
|
+
* @param {Record<string, any>[]} documents Project documents.
|
|
78
|
+
* @returns {{ format: string, entryNames: string[] } & Record<string, any>} Normalized source.
|
|
79
|
+
*/
|
|
80
|
+
static #source(source, documents) {
|
|
81
|
+
const provided = cloneSafeValue(source, {})
|
|
82
|
+
const entryNames = Array.isArray(provided.entryNames)
|
|
83
|
+
? provided.entryNames.map(String)
|
|
84
|
+
: documents
|
|
85
|
+
.map((document) => document?.source?.fileName)
|
|
86
|
+
.filter(Boolean)
|
|
87
|
+
.map(String)
|
|
88
|
+
return {
|
|
89
|
+
...provided,
|
|
90
|
+
format: String(
|
|
91
|
+
provided.format || documents[0]?.source?.format || 'circuitjson'
|
|
92
|
+
).toLowerCase(),
|
|
93
|
+
entryNames
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const IS_PROXY = (() => {
|
|
2
|
+
try {
|
|
3
|
+
const runtime = globalThis.process
|
|
4
|
+
const getBuiltinModule = Object.getOwnPropertyDescriptor(
|
|
5
|
+
runtime,
|
|
6
|
+
'getBuiltinModule'
|
|
7
|
+
)?.value
|
|
8
|
+
if (typeof getBuiltinModule !== 'function') return null
|
|
9
|
+
const utilities = Reflect.apply(getBuiltinModule, runtime, [
|
|
10
|
+
'node:util'
|
|
11
|
+
])
|
|
12
|
+
const types = Object.getOwnPropertyDescriptor(utilities, 'types')?.value
|
|
13
|
+
const detector = Object.getOwnPropertyDescriptor(
|
|
14
|
+
types,
|
|
15
|
+
'isProxy'
|
|
16
|
+
)?.value
|
|
17
|
+
return typeof detector === 'function' ? detector : null
|
|
18
|
+
} catch {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
})()
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Rejects executable Proxy containers before descriptor-based data handling.
|
|
25
|
+
*/
|
|
26
|
+
export class RuntimeProxyBoundary {
|
|
27
|
+
/**
|
|
28
|
+
* Rejects a Proxy without executing its traps when the host exposes a
|
|
29
|
+
* trusted intrinsic detector. Browser message cloning remains the final
|
|
30
|
+
* boundary in hosts without that optional detector.
|
|
31
|
+
* @param {unknown} value Candidate value.
|
|
32
|
+
* @param {string} label Human-readable value label.
|
|
33
|
+
* @returns {void}
|
|
34
|
+
*/
|
|
35
|
+
static assert(value, label) {
|
|
36
|
+
if (
|
|
37
|
+
IS_PROXY &&
|
|
38
|
+
value !== null &&
|
|
39
|
+
['object', 'function'].includes(typeof value) &&
|
|
40
|
+
Reflect.apply(IS_PROXY, undefined, [value])
|
|
41
|
+
) {
|
|
42
|
+
throw new TypeError(`${label} must not be a Proxy.`)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Object.freeze(RuntimeProxyBoundary.prototype)
|
|
48
|
+
Object.freeze(RuntimeProxyBoundary)
|