@opengeni/artifact-tool 0.2.2
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/LICENSE +190 -0
- package/README.md +104 -0
- package/dist/bounded-zip.d.ts +28 -0
- package/dist/chunk-4BSENMAE.js +39 -0
- package/dist/chunk-4BSENMAE.js.map +1 -0
- package/dist/chunk-4FRFNNBM.js +276 -0
- package/dist/chunk-4FRFNNBM.js.map +1 -0
- package/dist/chunk-5XIA6KHZ.js +49 -0
- package/dist/chunk-5XIA6KHZ.js.map +1 -0
- package/dist/chunk-66CT2ZRP.js +2229 -0
- package/dist/chunk-66CT2ZRP.js.map +1 -0
- package/dist/chunk-72SYDU4O.js +319 -0
- package/dist/chunk-72SYDU4O.js.map +1 -0
- package/dist/chunk-B2ZJRZDA.js +279 -0
- package/dist/chunk-B2ZJRZDA.js.map +1 -0
- package/dist/chunk-DCTLTNPW.js +2458 -0
- package/dist/chunk-DCTLTNPW.js.map +1 -0
- package/dist/chunk-FGZM4FQ6.js +473 -0
- package/dist/chunk-FGZM4FQ6.js.map +1 -0
- package/dist/chunk-HTJBLBDE.js +591 -0
- package/dist/chunk-HTJBLBDE.js.map +1 -0
- package/dist/chunk-IYFXZQ34.js +5306 -0
- package/dist/chunk-IYFXZQ34.js.map +1 -0
- package/dist/chunk-J6N7IR44.js +172 -0
- package/dist/chunk-J6N7IR44.js.map +1 -0
- package/dist/chunk-N2IHNWBS.js +1002 -0
- package/dist/chunk-N2IHNWBS.js.map +1 -0
- package/dist/chunk-NAG44K67.js +549 -0
- package/dist/chunk-NAG44K67.js.map +1 -0
- package/dist/chunk-O24KTEGP.js +33 -0
- package/dist/chunk-O24KTEGP.js.map +1 -0
- package/dist/chunk-PI2AZVGK.js +2074 -0
- package/dist/chunk-PI2AZVGK.js.map +1 -0
- package/dist/chunk-Q2FFHEY7.js +40 -0
- package/dist/chunk-Q2FFHEY7.js.map +1 -0
- package/dist/chunk-RBGQADXA.js +57 -0
- package/dist/chunk-RBGQADXA.js.map +1 -0
- package/dist/chunk-U5G2BKM2.js +613 -0
- package/dist/chunk-U5G2BKM2.js.map +1 -0
- package/dist/chunk-VOJHMSM3.js +39 -0
- package/dist/chunk-VOJHMSM3.js.map +1 -0
- package/dist/chunk-VUNV25KB.js +16 -0
- package/dist/chunk-VUNV25KB.js.map +1 -0
- package/dist/chunk-XIFUNVBO.js +101 -0
- package/dist/chunk-XIFUNVBO.js.map +1 -0
- package/dist/chunk-ZCJYRJGB.js +489 -0
- package/dist/chunk-ZCJYRJGB.js.map +1 -0
- package/dist/chunk-ZZXPEE4S.js +2260 -0
- package/dist/chunk-ZZXPEE4S.js.map +1 -0
- package/dist/document-docx-api.d.ts +49 -0
- package/dist/document-docx-codec.d.ts +9 -0
- package/dist/document-docx-codec.js +4765 -0
- package/dist/document-docx-codec.js.map +1 -0
- package/dist/document-docx-import.d.ts +261 -0
- package/dist/document-docx-packer.d.ts +18 -0
- package/dist/document-docx-state.d.ts +12 -0
- package/dist/document-render.d.ts +3 -0
- package/dist/document-render.js +492 -0
- package/dist/document-render.js.map +1 -0
- package/dist/document.d.ts +432 -0
- package/dist/document.js +37 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/file-blob.d.ts +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +12 -0
- package/dist/kernel.d.ts +101 -0
- package/dist/lib-GQLPACFK.js +9746 -0
- package/dist/lib-GQLPACFK.js.map +1 -0
- package/dist/materializer-cli-entry.d.ts +2 -0
- package/dist/materializer-cli-entry.js +29 -0
- package/dist/materializer-cli-entry.js.map +1 -0
- package/dist/materializer-cli.d.ts +24 -0
- package/dist/materializer-cli.js +24 -0
- package/dist/materializer-cli.js.map +1 -0
- package/dist/native-raster.d.ts +10 -0
- package/dist/native.d.ts +129 -0
- package/dist/native.js +31 -0
- package/dist/native.js.map +1 -0
- package/dist/office-import.d.ts +48 -0
- package/dist/office-import.js +154 -0
- package/dist/office-import.js.map +1 -0
- package/dist/presentation-pptx-api.d.ts +60 -0
- package/dist/presentation-pptx-import.d.ts +5 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
- package/dist/presentation-pptx-state-digest.d.ts +3 -0
- package/dist/presentation-pptx-state.d.ts +4 -0
- package/dist/presentation-pptx-xml.d.ts +29 -0
- package/dist/presentation-pptx.d.ts +5 -0
- package/dist/presentation-pptx.js +2365 -0
- package/dist/presentation-pptx.js.map +1 -0
- package/dist/presentation-render.d.ts +29 -0
- package/dist/presentation-render.js +375 -0
- package/dist/presentation-render.js.map +1 -0
- package/dist/presentation.d.ts +565 -0
- package/dist/presentation.js +71 -0
- package/dist/presentation.js.map +1 -0
- package/dist/production-composite.d.ts +68 -0
- package/dist/production-document.d.ts +2 -0
- package/dist/production-document.js +176 -0
- package/dist/production-document.js.map +1 -0
- package/dist/production-native-codecs.d.ts +94 -0
- package/dist/production-presentation.d.ts +2 -0
- package/dist/production-presentation.js +176 -0
- package/dist/production-presentation.js.map +1 -0
- package/dist/production-sha256.d.ts +2 -0
- package/dist/production-spreadsheet-native.d.ts +12 -0
- package/dist/production-spreadsheet.d.ts +2 -0
- package/dist/production-spreadsheet.js +176 -0
- package/dist/production-spreadsheet.js.map +1 -0
- package/dist/production.d.ts +96 -0
- package/dist/raster-image.d.ts +34 -0
- package/dist/reference.d.ts +17 -0
- package/dist/reference.js +574 -0
- package/dist/reference.js.map +1 -0
- package/dist/runtime-cli-entry.d.ts +2 -0
- package/dist/runtime-cli-entry.js +21 -0
- package/dist/runtime-cli-entry.js.map +1 -0
- package/dist/runtime-cli.d.ts +76 -0
- package/dist/runtime-cli.js +26 -0
- package/dist/runtime-cli.js.map +1 -0
- package/dist/runtime-development.d.ts +73 -0
- package/dist/runtime-development.js +30 -0
- package/dist/runtime-development.js.map +1 -0
- package/dist/runtime-receipt.d.ts +19 -0
- package/dist/runtime.d.ts +355 -0
- package/dist/runtime.js +42 -0
- package/dist/runtime.js.map +1 -0
- package/dist/snapshot.d.ts +26 -0
- package/dist/snapshot.js +61 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/spreadsheet-address.d.ts +18 -0
- package/dist/spreadsheet-file.d.ts +17 -0
- package/dist/spreadsheet-formula.d.ts +67 -0
- package/dist/spreadsheet-image.d.ts +17 -0
- package/dist/spreadsheet-render.d.ts +5 -0
- package/dist/spreadsheet-render.js +752 -0
- package/dist/spreadsheet-render.js.map +1 -0
- package/dist/spreadsheet-types.d.ts +155 -0
- package/dist/spreadsheet-xlsx-api.d.ts +88 -0
- package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
- package/dist/spreadsheet-xlsx-codec.js +16 -0
- package/dist/spreadsheet-xlsx-codec.js.map +1 -0
- package/dist/spreadsheet-xlsx-state.d.ts +16 -0
- package/dist/spreadsheet.d.ts +724 -0
- package/dist/spreadsheet.js +67 -0
- package/dist/spreadsheet.js.map +1 -0
- package/package.json +129 -0
- package/src/bounded-zip.ts +450 -0
- package/src/document-docx-api.ts +64 -0
- package/src/document-docx-codec.ts +2253 -0
- package/src/document-docx-import.ts +3814 -0
- package/src/document-docx-packer.ts +72 -0
- package/src/document-docx-state.ts +59 -0
- package/src/document-render.ts +615 -0
- package/src/document.ts +2800 -0
- package/src/errors.ts +45 -0
- package/src/file-blob.ts +43 -0
- package/src/index.ts +4 -0
- package/src/inspection.ts +50 -0
- package/src/kernel.ts +525 -0
- package/src/materializer-cli-entry.ts +11 -0
- package/src/materializer-cli.ts +743 -0
- package/src/native-raster.ts +141 -0
- package/src/native.ts +390 -0
- package/src/office-import.ts +243 -0
- package/src/presentation-pptx-api.ts +107 -0
- package/src/presentation-pptx-import.ts +2065 -0
- package/src/presentation-pptx-state-digest.ts +46 -0
- package/src/presentation-pptx-state.ts +18 -0
- package/src/presentation-pptx-xml.ts +473 -0
- package/src/presentation-pptx.ts +447 -0
- package/src/presentation-render.ts +532 -0
- package/src/presentation.ts +2599 -0
- package/src/production-composite.ts +805 -0
- package/src/production-document.ts +2 -0
- package/src/production-native-codecs.ts +1141 -0
- package/src/production-presentation.ts +2 -0
- package/src/production-sha256.ts +150 -0
- package/src/production-spreadsheet-native.ts +686 -0
- package/src/production-spreadsheet.ts +2 -0
- package/src/production.ts +1022 -0
- package/src/raster-image.ts +586 -0
- package/src/reference.ts +17 -0
- package/src/runtime-cli-entry.ts +13 -0
- package/src/runtime-cli.ts +670 -0
- package/src/runtime-development.ts +689 -0
- package/src/runtime-receipt.ts +169 -0
- package/src/runtime.ts +1463 -0
- package/src/snapshot.ts +88 -0
- package/src/spreadsheet-address.ts +90 -0
- package/src/spreadsheet-file.ts +71 -0
- package/src/spreadsheet-formula.ts +1212 -0
- package/src/spreadsheet-image.ts +242 -0
- package/src/spreadsheet-render.ts +985 -0
- package/src/spreadsheet-types.ts +171 -0
- package/src/spreadsheet-xlsx-api.ts +123 -0
- package/src/spreadsheet-xlsx-codec.ts +3063 -0
- package/src/spreadsheet-xlsx-state.ts +191 -0
- package/src/spreadsheet.ts +5195 -0
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
import type { WorkbookChange } from "./spreadsheet";
|
|
2
|
+
import type {
|
|
3
|
+
NativeArtifactSession,
|
|
4
|
+
NativeDocumentSession,
|
|
5
|
+
NativePresentationSession,
|
|
6
|
+
NativeSpreadsheetSession,
|
|
7
|
+
} from "./native";
|
|
8
|
+
|
|
9
|
+
const ROOT_KEY = "$root";
|
|
10
|
+
const PROXY_METADATA = new WeakMap<object, ProxyMetadata>();
|
|
11
|
+
|
|
12
|
+
export type CompositeModality = "spreadsheet" | "document" | "presentation";
|
|
13
|
+
|
|
14
|
+
export type CompositeReconciliation = Readonly<{
|
|
15
|
+
session: NativeArtifactSession;
|
|
16
|
+
data?: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
export type CompositeReconciler<Root extends object> = (root: Root) => CompositeReconciliation;
|
|
20
|
+
|
|
21
|
+
export type CompositePropertyRead = Readonly<{
|
|
22
|
+
handled: boolean;
|
|
23
|
+
value?: unknown;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
type Locator<Root extends object> = Readonly<{
|
|
27
|
+
key: string;
|
|
28
|
+
resolve(root: Root): unknown;
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
type ProxyMetadata = Readonly<{
|
|
32
|
+
state: CompositeArtifactState<object>;
|
|
33
|
+
locator: Locator<object>;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export type CompositeMutation = Readonly<{
|
|
37
|
+
member: PropertyKey;
|
|
38
|
+
owner: unknown;
|
|
39
|
+
arguments?: readonly unknown[];
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
export type PreparedCompositeMutation = Readonly<{
|
|
43
|
+
/** Applies one already-prevalidated mutation to the retained native session. */
|
|
44
|
+
commit(result: unknown): boolean;
|
|
45
|
+
}>;
|
|
46
|
+
|
|
47
|
+
export type CompositeMutationPreparer<Root extends object> = (
|
|
48
|
+
mutation: CompositeMutation,
|
|
49
|
+
state: CompositeArtifactState<Root>,
|
|
50
|
+
) => PreparedCompositeMutation | null;
|
|
51
|
+
|
|
52
|
+
const QUERY_METHODS = new Set<PropertyKey>([
|
|
53
|
+
"allStoryBlocks",
|
|
54
|
+
"arrayBuffer",
|
|
55
|
+
"at",
|
|
56
|
+
"entries",
|
|
57
|
+
"every",
|
|
58
|
+
"export",
|
|
59
|
+
"filter",
|
|
60
|
+
"find",
|
|
61
|
+
"findIndex",
|
|
62
|
+
"findLast",
|
|
63
|
+
"findLastIndex",
|
|
64
|
+
"forBlock",
|
|
65
|
+
"forEach",
|
|
66
|
+
"getActiveWorksheet",
|
|
67
|
+
"getCell",
|
|
68
|
+
"getItem",
|
|
69
|
+
"getItemAt",
|
|
70
|
+
"getRange",
|
|
71
|
+
"getRangeByIndexes",
|
|
72
|
+
"getUsedRange",
|
|
73
|
+
"help",
|
|
74
|
+
"includes",
|
|
75
|
+
"indexOf",
|
|
76
|
+
"inspect",
|
|
77
|
+
"inspectRecord",
|
|
78
|
+
"join",
|
|
79
|
+
"keys",
|
|
80
|
+
"lastIndexOf",
|
|
81
|
+
"layoutSnapshot",
|
|
82
|
+
"map",
|
|
83
|
+
"ownsObject",
|
|
84
|
+
"reduce",
|
|
85
|
+
"reduceRight",
|
|
86
|
+
"render",
|
|
87
|
+
"resolve",
|
|
88
|
+
"resize",
|
|
89
|
+
"serialize",
|
|
90
|
+
"slice",
|
|
91
|
+
"some",
|
|
92
|
+
"summary",
|
|
93
|
+
"text",
|
|
94
|
+
"toJSON",
|
|
95
|
+
"toLocaleString",
|
|
96
|
+
"toProto",
|
|
97
|
+
"toString",
|
|
98
|
+
"trace",
|
|
99
|
+
"values",
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
const ARRAY_CALLBACK_METHODS = new Set<PropertyKey>([
|
|
103
|
+
"every",
|
|
104
|
+
"filter",
|
|
105
|
+
"find",
|
|
106
|
+
"findIndex",
|
|
107
|
+
"findLast",
|
|
108
|
+
"findLastIndex",
|
|
109
|
+
"flatMap",
|
|
110
|
+
"forEach",
|
|
111
|
+
"map",
|
|
112
|
+
"reduce",
|
|
113
|
+
"reduceRight",
|
|
114
|
+
"some",
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
const ARRAY_MUTATION_METHODS = new Set<PropertyKey>([
|
|
118
|
+
"copyWithin",
|
|
119
|
+
"fill",
|
|
120
|
+
"pop",
|
|
121
|
+
"push",
|
|
122
|
+
"reverse",
|
|
123
|
+
"shift",
|
|
124
|
+
"sort",
|
|
125
|
+
"splice",
|
|
126
|
+
"unshift",
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Owns one host projection and one exact native session. Public objects are
|
|
131
|
+
* location proxies, not aliases to a particular projection instance. Rare
|
|
132
|
+
* reconciliation mutations can therefore replace the host graph atomically;
|
|
133
|
+
* hot mutations retain both the host graph and native session.
|
|
134
|
+
*/
|
|
135
|
+
export class CompositeArtifactState<Root extends object> {
|
|
136
|
+
readonly modality: CompositeModality;
|
|
137
|
+
readonly namespace: bigint;
|
|
138
|
+
|
|
139
|
+
#root: Root;
|
|
140
|
+
#native: NativeArtifactSession;
|
|
141
|
+
#nativeData: unknown;
|
|
142
|
+
#revision: number;
|
|
143
|
+
readonly #disposedError: Error;
|
|
144
|
+
#disposed = false;
|
|
145
|
+
#mutationDepth = 0;
|
|
146
|
+
#asyncMutation = false;
|
|
147
|
+
readonly #reconcileProjection: CompositeReconciler<Root>;
|
|
148
|
+
readonly #prepareMutation: CompositeMutationPreparer<Root> | undefined;
|
|
149
|
+
readonly #proxies = new Map<string, object>();
|
|
150
|
+
readonly #functions = new Map<string, (...args: unknown[]) => unknown>();
|
|
151
|
+
readonly #changeListeners = new Set<(change: WorkbookChange) => void>();
|
|
152
|
+
readonly #installProjection:
|
|
153
|
+
| ((root: Root, state: CompositeArtifactState<Root>) => void)
|
|
154
|
+
| undefined;
|
|
155
|
+
readonly #readProperty:
|
|
156
|
+
| ((
|
|
157
|
+
owner: unknown,
|
|
158
|
+
member: PropertyKey,
|
|
159
|
+
state: CompositeArtifactState<Root>,
|
|
160
|
+
) => CompositePropertyRead)
|
|
161
|
+
| undefined;
|
|
162
|
+
readonly #captureAuxiliary: ((root: Root) => unknown) | undefined;
|
|
163
|
+
readonly #restoreAuxiliary: ((root: Root, value: unknown) => void) | undefined;
|
|
164
|
+
|
|
165
|
+
constructor(
|
|
166
|
+
input: Readonly<{
|
|
167
|
+
modality: CompositeModality;
|
|
168
|
+
namespace: bigint;
|
|
169
|
+
root: Root;
|
|
170
|
+
reconciliation: CompositeReconciliation;
|
|
171
|
+
reconcile: CompositeReconciler<Root>;
|
|
172
|
+
prepareMutation?: CompositeMutationPreparer<Root>;
|
|
173
|
+
installProjection?: (root: Root, state: CompositeArtifactState<Root>) => void;
|
|
174
|
+
readProperty?: (
|
|
175
|
+
owner: unknown,
|
|
176
|
+
member: PropertyKey,
|
|
177
|
+
state: CompositeArtifactState<Root>,
|
|
178
|
+
) => CompositePropertyRead;
|
|
179
|
+
captureAuxiliary?: (root: Root) => unknown;
|
|
180
|
+
restoreAuxiliary?: (root: Root, value: unknown) => void;
|
|
181
|
+
}>,
|
|
182
|
+
) {
|
|
183
|
+
this.modality = input.modality;
|
|
184
|
+
this.namespace = input.namespace;
|
|
185
|
+
this.#root = input.root;
|
|
186
|
+
this.#native = input.reconciliation.session;
|
|
187
|
+
this.#nativeData = input.reconciliation.data;
|
|
188
|
+
this.#revision = safeRevision(this.#native.revision());
|
|
189
|
+
this.#reconcileProjection = input.reconcile;
|
|
190
|
+
this.#prepareMutation = input.prepareMutation;
|
|
191
|
+
this.#installProjection = input.installProjection;
|
|
192
|
+
this.#readProperty = input.readProperty;
|
|
193
|
+
this.#captureAuxiliary = input.captureAuxiliary;
|
|
194
|
+
this.#restoreAuxiliary = input.restoreAuxiliary;
|
|
195
|
+
this.#disposedError = new Error(`${input.modality} artifact is disposed`);
|
|
196
|
+
this.#installProjection?.(this.#root, this);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
get root(): Root {
|
|
200
|
+
this.assertUsable();
|
|
201
|
+
return this.#root;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
get native(): NativeArtifactSession {
|
|
205
|
+
this.assertUsable();
|
|
206
|
+
return this.#native;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get nativeData(): unknown {
|
|
210
|
+
this.assertUsable();
|
|
211
|
+
return this.#nativeData;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Monotonic facade revision across retained and rare replacement sessions. */
|
|
215
|
+
get revision(): number {
|
|
216
|
+
this.assertUsable();
|
|
217
|
+
return this.#revision;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** True only inside one explicit/public mutation transaction. */
|
|
221
|
+
get inMutation(): boolean {
|
|
222
|
+
return this.#mutationDepth > 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
proxy(): Root {
|
|
226
|
+
return this.wrap(rootLocator()) as Root;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
rawRoot(): Root {
|
|
230
|
+
this.assertUsable();
|
|
231
|
+
return this.#root;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
assertUsable(): void {
|
|
235
|
+
if (this.#disposed) throw this.#disposedError;
|
|
236
|
+
if (this.#asyncMutation && this.#mutationDepth === 0) {
|
|
237
|
+
throw new Error(`${this.modality} artifact mutation is still in progress`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
onWorkbookChange(listener: (change: WorkbookChange) => void): () => void {
|
|
242
|
+
this.assertUsable();
|
|
243
|
+
if (this.modality !== "spreadsheet") {
|
|
244
|
+
throw new Error("Workbook change listeners are only available for spreadsheets");
|
|
245
|
+
}
|
|
246
|
+
this.#changeListeners.add(listener);
|
|
247
|
+
return () => this.#changeListeners.delete(listener);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
dispose(): void {
|
|
251
|
+
if (this.#disposed) return;
|
|
252
|
+
this.#native.dispose();
|
|
253
|
+
this.#changeListeners.clear();
|
|
254
|
+
this.#proxies.clear();
|
|
255
|
+
this.#functions.clear();
|
|
256
|
+
this.#disposed = true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
mutate<Result>(operation: () => Result, hint: CompositeMutation): Result {
|
|
260
|
+
this.assertUsable();
|
|
261
|
+
if (this.#mutationDepth > 0) return operation();
|
|
262
|
+
|
|
263
|
+
const prepared = this.#prepareMutation?.(hint, this) ?? null;
|
|
264
|
+
const acceptedRoot = prepared ? null : cloneProjectionGraph(this.#root);
|
|
265
|
+
const acceptedAuxiliary = prepared ? undefined : this.#captureAuxiliary?.(this.#root);
|
|
266
|
+
this.#mutationDepth = 1;
|
|
267
|
+
let result: Result;
|
|
268
|
+
try {
|
|
269
|
+
result = operation();
|
|
270
|
+
} catch (cause) {
|
|
271
|
+
if (acceptedRoot) this.#restoreProjection(acceptedRoot, acceptedAuxiliary);
|
|
272
|
+
this.#mutationDepth = 0;
|
|
273
|
+
throw cause;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (isPromiseLike(result)) {
|
|
277
|
+
this.#asyncMutation = true;
|
|
278
|
+
return result.then(
|
|
279
|
+
(value) => {
|
|
280
|
+
try {
|
|
281
|
+
this.#acceptProjection(hint, prepared, value);
|
|
282
|
+
return value;
|
|
283
|
+
} catch (cause) {
|
|
284
|
+
if (acceptedRoot) this.#restoreProjection(acceptedRoot, acceptedAuxiliary);
|
|
285
|
+
else this.#poisonAfterIncrementalFailure();
|
|
286
|
+
throw cause;
|
|
287
|
+
} finally {
|
|
288
|
+
this.#mutationDepth = 0;
|
|
289
|
+
this.#asyncMutation = false;
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
(cause) => {
|
|
293
|
+
if (acceptedRoot) this.#restoreProjection(acceptedRoot, acceptedAuxiliary);
|
|
294
|
+
this.#mutationDepth = 0;
|
|
295
|
+
this.#asyncMutation = false;
|
|
296
|
+
throw cause;
|
|
297
|
+
},
|
|
298
|
+
) as Result;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
this.#acceptProjection(hint, prepared, result);
|
|
303
|
+
return result;
|
|
304
|
+
} catch (cause) {
|
|
305
|
+
if (acceptedRoot) this.#restoreProjection(acceptedRoot, acceptedAuxiliary);
|
|
306
|
+
else this.#poisonAfterIncrementalFailure();
|
|
307
|
+
throw cause;
|
|
308
|
+
} finally {
|
|
309
|
+
this.#mutationDepth = 0;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
wrap(locator: Locator<Root>): unknown {
|
|
314
|
+
this.assertUsable();
|
|
315
|
+
const existing = this.#proxies.get(locator.key);
|
|
316
|
+
if (existing) return existing;
|
|
317
|
+
const current = locator.resolve(this.#root);
|
|
318
|
+
if (!isProxyCandidate(current)) return current;
|
|
319
|
+
|
|
320
|
+
const target: object = Array.isArray(current) ? [] : {};
|
|
321
|
+
const proxy = new Proxy(target, this.#handler(locator));
|
|
322
|
+
this.#proxies.set(locator.key, proxy);
|
|
323
|
+
PROXY_METADATA.set(proxy, {
|
|
324
|
+
state: this as unknown as CompositeArtifactState<object>,
|
|
325
|
+
locator: locator as unknown as Locator<object>,
|
|
326
|
+
});
|
|
327
|
+
return proxy;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
#handler(locator: Locator<Root>): ProxyHandler<object> {
|
|
331
|
+
return {
|
|
332
|
+
get: (_target, member) => {
|
|
333
|
+
this.assertUsable();
|
|
334
|
+
const owner = locator.resolve(this.#root);
|
|
335
|
+
if (!isObject(owner)) return undefined;
|
|
336
|
+
|
|
337
|
+
if (locator.key === ROOT_KEY) {
|
|
338
|
+
if (member === "batch") {
|
|
339
|
+
return this.#rootFunction("batch", (callback: unknown) => {
|
|
340
|
+
if (typeof callback !== "function") {
|
|
341
|
+
throw new TypeError("Artifact batch callback must be a function");
|
|
342
|
+
}
|
|
343
|
+
return this.mutate(() => callback(this.proxy()), {
|
|
344
|
+
member: "batch",
|
|
345
|
+
owner: this.#root,
|
|
346
|
+
arguments: [callback],
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
if (member === "dispose") return this.#rootFunction("dispose", () => this.dispose());
|
|
351
|
+
if (member === "onChange" && this.modality === "spreadsheet") {
|
|
352
|
+
return this.#rootFunction("onChange", (listener: unknown) => {
|
|
353
|
+
if (typeof listener !== "function")
|
|
354
|
+
throw new TypeError("Workbook listener must be a function");
|
|
355
|
+
return this.onWorkbookChange(listener as (change: WorkbookChange) => void);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (member === "recalculate" && this.modality === "spreadsheet") {
|
|
359
|
+
return this.#rootFunction("recalculate", () => undefined);
|
|
360
|
+
}
|
|
361
|
+
if (
|
|
362
|
+
member === "revision" &&
|
|
363
|
+
(this.modality === "spreadsheet" || this.modality === "document")
|
|
364
|
+
) {
|
|
365
|
+
return this.#revision;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (Array.isArray(owner) && member === Symbol.iterator) {
|
|
370
|
+
return this.#arrayIterator(locator, owner);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const projected = this.#readProperty?.(owner, member, this);
|
|
374
|
+
const value =
|
|
375
|
+
projected?.handled === true ? projected.value : Reflect.get(owner, member, owner);
|
|
376
|
+
if (typeof value === "function") {
|
|
377
|
+
const functionKey = `${locator.key}.${propertyKey(member)}`;
|
|
378
|
+
const cached = this.#functions.get(functionKey);
|
|
379
|
+
if (cached) return cached;
|
|
380
|
+
const wrapped = (...args: unknown[]): unknown => {
|
|
381
|
+
this.assertUsable();
|
|
382
|
+
const currentOwner = locator.resolve(this.#root);
|
|
383
|
+
if (!isObject(currentOwner)) throw new Error("Artifact object no longer exists");
|
|
384
|
+
const currentMethod = Reflect.get(currentOwner, member, currentOwner);
|
|
385
|
+
if (typeof currentMethod !== "function")
|
|
386
|
+
throw new TypeError(`${String(member)} is not callable`);
|
|
387
|
+
const invocationArgs =
|
|
388
|
+
Array.isArray(currentOwner) && ARRAY_CALLBACK_METHODS.has(member)
|
|
389
|
+
? this.#arrayCallbackArguments(locator, args)
|
|
390
|
+
: args.map(unwrapArtifactValue);
|
|
391
|
+
const invoke = () => currentMethod.apply(currentOwner, invocationArgs);
|
|
392
|
+
const mutates = Array.isArray(currentOwner)
|
|
393
|
+
? ARRAY_MUTATION_METHODS.has(member)
|
|
394
|
+
: !QUERY_METHODS.has(member);
|
|
395
|
+
const result = mutates
|
|
396
|
+
? this.mutate(invoke, { member, owner: currentOwner, arguments: invocationArgs })
|
|
397
|
+
: invoke();
|
|
398
|
+
return this.#wrapReturn(locator, member, result);
|
|
399
|
+
};
|
|
400
|
+
this.#functions.set(functionKey, wrapped);
|
|
401
|
+
return wrapped;
|
|
402
|
+
}
|
|
403
|
+
return this.#wrapProperty(locator, member, value);
|
|
404
|
+
},
|
|
405
|
+
set: (_target, member, value) => {
|
|
406
|
+
this.assertUsable();
|
|
407
|
+
const owner = locator.resolve(this.#root);
|
|
408
|
+
if (!isObject(owner)) throw new Error("Artifact object no longer exists");
|
|
409
|
+
return this.mutate(() => Reflect.set(owner, member, unwrapArtifactValue(value), owner), {
|
|
410
|
+
member,
|
|
411
|
+
owner,
|
|
412
|
+
arguments: [unwrapArtifactValue(value)],
|
|
413
|
+
});
|
|
414
|
+
},
|
|
415
|
+
deleteProperty: (_target, member) => {
|
|
416
|
+
this.assertUsable();
|
|
417
|
+
const owner = locator.resolve(this.#root);
|
|
418
|
+
if (!isObject(owner)) throw new Error("Artifact object no longer exists");
|
|
419
|
+
return this.mutate(() => Reflect.deleteProperty(owner, member), {
|
|
420
|
+
member,
|
|
421
|
+
owner,
|
|
422
|
+
arguments: [],
|
|
423
|
+
});
|
|
424
|
+
},
|
|
425
|
+
has: (_target, member) => {
|
|
426
|
+
const owner = locator.resolve(this.#root);
|
|
427
|
+
return isObject(owner) && member in owner;
|
|
428
|
+
},
|
|
429
|
+
getPrototypeOf: () => {
|
|
430
|
+
const owner = locator.resolve(this.#root);
|
|
431
|
+
return isObject(owner) ? Object.getPrototypeOf(owner) : null;
|
|
432
|
+
},
|
|
433
|
+
ownKeys: (target) => {
|
|
434
|
+
const owner = locator.resolve(this.#root);
|
|
435
|
+
const keys = isObject(owner) ? Reflect.ownKeys(owner) : [];
|
|
436
|
+
if (Array.isArray(target) && !keys.includes("length")) keys.push("length");
|
|
437
|
+
return keys;
|
|
438
|
+
},
|
|
439
|
+
getOwnPropertyDescriptor: (target, member) => {
|
|
440
|
+
if (Array.isArray(target) && member === "length") {
|
|
441
|
+
return Reflect.getOwnPropertyDescriptor(target, "length");
|
|
442
|
+
}
|
|
443
|
+
const owner = locator.resolve(this.#root);
|
|
444
|
+
const descriptor = isObject(owner)
|
|
445
|
+
? Reflect.getOwnPropertyDescriptor(owner, member)
|
|
446
|
+
: undefined;
|
|
447
|
+
return descriptor ? { ...descriptor, configurable: true } : undefined;
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
#acceptProjection(
|
|
453
|
+
hint: CompositeMutation,
|
|
454
|
+
prepared: PreparedCompositeMutation | null,
|
|
455
|
+
result: unknown,
|
|
456
|
+
): void {
|
|
457
|
+
if (prepared) {
|
|
458
|
+
const changed = prepared.commit(result);
|
|
459
|
+
if (changed) {
|
|
460
|
+
this.#revision = Math.max(this.#revision + 1, safeRevision(this.#native.revision()));
|
|
461
|
+
this.#emitWorkbookChange(hint);
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const accepted = this.#reconcileProjection(this.#root);
|
|
466
|
+
const previous = this.#native;
|
|
467
|
+
this.#native = accepted.session;
|
|
468
|
+
this.#nativeData = accepted.data;
|
|
469
|
+
this.#revision = Math.max(this.#revision + 1, safeRevision(this.#native.revision()));
|
|
470
|
+
try {
|
|
471
|
+
previous.dispose();
|
|
472
|
+
} catch {
|
|
473
|
+
// The new session is already authoritative. Disposal is idempotent and
|
|
474
|
+
// cannot be allowed to roll a successful atomic projection swap back.
|
|
475
|
+
}
|
|
476
|
+
this.#emitWorkbookChange(hint);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
#poisonAfterIncrementalFailure(): void {
|
|
480
|
+
try {
|
|
481
|
+
this.#native.dispose();
|
|
482
|
+
} finally {
|
|
483
|
+
this.#disposed = true;
|
|
484
|
+
this.#changeListeners.clear();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
#restoreProjection(root: Root, auxiliary: unknown): void {
|
|
489
|
+
this.#root = root;
|
|
490
|
+
this.#installProjection?.(this.#root, this);
|
|
491
|
+
this.#restoreAuxiliary?.(this.#root, auxiliary);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
#emitWorkbookChange(hint: CompositeMutation): void {
|
|
495
|
+
if (this.modality !== "spreadsheet" || this.#changeListeners.size === 0) return;
|
|
496
|
+
const root = this.#root as unknown as {
|
|
497
|
+
worksheets?: { items?: ReadonlyArray<{ id?: string }> };
|
|
498
|
+
};
|
|
499
|
+
const owner = hint.owner as { id?: unknown; worksheet?: { id?: unknown } };
|
|
500
|
+
const ownerSheet =
|
|
501
|
+
typeof owner?.id === "string" && owner.id.startsWith("ws/")
|
|
502
|
+
? owner.id
|
|
503
|
+
: typeof owner?.worksheet?.id === "string"
|
|
504
|
+
? owner.worksheet.id
|
|
505
|
+
: null;
|
|
506
|
+
const sheetIds = ownerSheet
|
|
507
|
+
? [ownerSheet]
|
|
508
|
+
: (root.worksheets?.items ?? [])
|
|
509
|
+
.map((sheet) => sheet.id)
|
|
510
|
+
.filter((id): id is string => typeof id === "string");
|
|
511
|
+
const member = String(hint.member).toLowerCase();
|
|
512
|
+
const reason: WorkbookChange["reason"] =
|
|
513
|
+
member.includes("format") || member.includes("style")
|
|
514
|
+
? "format"
|
|
515
|
+
: member.includes("comment") || member.includes("reply")
|
|
516
|
+
? "comment"
|
|
517
|
+
: member.includes("width") || member.includes("height")
|
|
518
|
+
? "dimension"
|
|
519
|
+
: member.includes("chart") ||
|
|
520
|
+
member.includes("image") ||
|
|
521
|
+
member.includes("shape") ||
|
|
522
|
+
member.includes("sparkline")
|
|
523
|
+
? "drawing"
|
|
524
|
+
: member.includes("sheet") || member === "add" || member === "name"
|
|
525
|
+
? "structure"
|
|
526
|
+
: "content";
|
|
527
|
+
const change: WorkbookChange = Object.freeze({
|
|
528
|
+
revision: this.#revision,
|
|
529
|
+
sheetIds: Object.freeze([...new Set(sheetIds)].sort()),
|
|
530
|
+
reason,
|
|
531
|
+
});
|
|
532
|
+
for (const listener of this.#changeListeners) listener(change);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
#rootFunction(
|
|
536
|
+
name: string,
|
|
537
|
+
implementation: (...args: unknown[]) => unknown,
|
|
538
|
+
): (...args: unknown[]) => unknown {
|
|
539
|
+
const key = `${ROOT_KEY}.${name}`;
|
|
540
|
+
const existing = this.#functions.get(key);
|
|
541
|
+
if (existing) return existing;
|
|
542
|
+
this.#functions.set(key, implementation);
|
|
543
|
+
return implementation;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
#wrapProperty(parent: Locator<Root>, member: PropertyKey, value: unknown): unknown {
|
|
547
|
+
if (!isProxyCandidate(value)) return value;
|
|
548
|
+
const stable = this.#stableLocator(value);
|
|
549
|
+
return this.wrap(stable ?? propertyLocator(parent, member));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
#wrapReturn(parent: Locator<Root>, member: PropertyKey, value: unknown): unknown {
|
|
553
|
+
if (isPromiseLike(value)) {
|
|
554
|
+
return value.then((resolved) => this.#wrapReturn(parent, member, resolved));
|
|
555
|
+
}
|
|
556
|
+
if (!isProxyCandidate(value)) return value;
|
|
557
|
+
const stable = this.#stableLocator(value);
|
|
558
|
+
if (stable) return this.wrap(stable);
|
|
559
|
+
if (isRangeLike(value)) return this.wrap(rangeLocator(value));
|
|
560
|
+
// Arrays and plain records returned by inspection/serialization are owned
|
|
561
|
+
// copies, not live graph nodes. Only property traversal creates a locator
|
|
562
|
+
// for such values.
|
|
563
|
+
return value;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
#stableLocator(value: object): Locator<Root> | null {
|
|
567
|
+
const id = Reflect.get(value, "id");
|
|
568
|
+
if (typeof id !== "string") return null;
|
|
569
|
+
const root = this.#root as unknown as { resolve?: (id: string) => unknown };
|
|
570
|
+
if (typeof root.resolve !== "function") return null;
|
|
571
|
+
try {
|
|
572
|
+
if (root.resolve(id) !== value) return null;
|
|
573
|
+
} catch {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
key: `id:${id}`,
|
|
578
|
+
resolve: (candidateRoot) => {
|
|
579
|
+
const resolve = (candidateRoot as unknown as { resolve?: (objectId: string) => unknown })
|
|
580
|
+
.resolve;
|
|
581
|
+
if (typeof resolve !== "function") throw new Error(`Artifact cannot resolve ${id}`);
|
|
582
|
+
return resolve.call(candidateRoot, id);
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
#arrayIterator(locator: Locator<Root>, owner: unknown[]): () => IterableIterator<unknown> {
|
|
588
|
+
const current = () => locator.resolve(this.#root);
|
|
589
|
+
const wrap = (index: number, value: unknown) =>
|
|
590
|
+
this.#wrapProperty(locator, String(index), value);
|
|
591
|
+
return function* iterator(): IterableIterator<unknown> {
|
|
592
|
+
for (let index = 0; index < owner.length; index += 1) {
|
|
593
|
+
const currentArray = current();
|
|
594
|
+
if (!Array.isArray(currentArray) || index >= currentArray.length) return;
|
|
595
|
+
yield wrap(index, currentArray[index]);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
#arrayCallbackArguments(locator: Locator<Root>, args: readonly unknown[]): unknown[] {
|
|
601
|
+
if (typeof args[0] !== "function") return args.map(unwrapArtifactValue);
|
|
602
|
+
const callback = args[0] as (...callbackArgs: unknown[]) => unknown;
|
|
603
|
+
const thisArg = args[1];
|
|
604
|
+
const wrapChild = (index: number, value: unknown) =>
|
|
605
|
+
this.#wrapProperty(locator, String(index), value);
|
|
606
|
+
const wrappedArray = () => this.wrap(locator);
|
|
607
|
+
const wrappedCallback = function (this: unknown, value: unknown, index: unknown) {
|
|
608
|
+
const child = typeof index === "number" ? wrapChild(index, value) : value;
|
|
609
|
+
return callback.call(thisArg ?? this, child, index, wrappedArray());
|
|
610
|
+
};
|
|
611
|
+
return [wrappedCallback, thisArg];
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export function stateOf(value: unknown): CompositeArtifactState<object> | null {
|
|
616
|
+
if (!isObject(value)) return null;
|
|
617
|
+
return PROXY_METADATA.get(value)?.state ?? null;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export function requireCompositeState(
|
|
621
|
+
value: unknown,
|
|
622
|
+
modality?: CompositeModality,
|
|
623
|
+
): CompositeArtifactState<object> {
|
|
624
|
+
const state = stateOf(value);
|
|
625
|
+
if (!state) throw new TypeError("Expected an artifact created by the production facade");
|
|
626
|
+
state.assertUsable();
|
|
627
|
+
if (modality && state.modality !== modality) {
|
|
628
|
+
throw new TypeError(`Expected a ${modality} artifact`);
|
|
629
|
+
}
|
|
630
|
+
return state;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
export function unwrapArtifactValue(value: unknown): unknown {
|
|
634
|
+
if (!isObject(value)) return value;
|
|
635
|
+
const metadata = PROXY_METADATA.get(value);
|
|
636
|
+
if (metadata) return metadata.locator.resolve(metadata.state.rawRoot());
|
|
637
|
+
if (Array.isArray(value)) return value.map(unwrapArtifactValue);
|
|
638
|
+
if (!isPlainObject(value)) return value;
|
|
639
|
+
const output: Record<PropertyKey, unknown> = {};
|
|
640
|
+
for (const key of Reflect.ownKeys(value))
|
|
641
|
+
output[key] = unwrapArtifactValue(Reflect.get(value, key));
|
|
642
|
+
return output;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export function nativeSessionOf(
|
|
646
|
+
state: CompositeArtifactState<object>,
|
|
647
|
+
): NativeSpreadsheetSession | NativeDocumentSession | NativePresentationSession {
|
|
648
|
+
return state.native;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function rootLocator<Root extends object>(): Locator<Root> {
|
|
652
|
+
return { key: ROOT_KEY, resolve: (root) => root };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function propertyLocator<Root extends object>(
|
|
656
|
+
parent: Locator<Root>,
|
|
657
|
+
member: PropertyKey,
|
|
658
|
+
): Locator<Root> {
|
|
659
|
+
return {
|
|
660
|
+
key: `${parent.key}.${propertyKey(member)}`,
|
|
661
|
+
resolve(root) {
|
|
662
|
+
const owner = parent.resolve(root);
|
|
663
|
+
if (!isObject(owner)) throw new Error("Artifact parent object no longer exists");
|
|
664
|
+
return Reflect.get(owner, member, owner);
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function rangeLocator<Root extends object>(range: object): Locator<Root> {
|
|
670
|
+
const worksheet = Reflect.get(range, "worksheet") as { id?: unknown };
|
|
671
|
+
const address = Reflect.get(range, "address") as {
|
|
672
|
+
row?: unknown;
|
|
673
|
+
col?: unknown;
|
|
674
|
+
rowCount?: unknown;
|
|
675
|
+
colCount?: unknown;
|
|
676
|
+
};
|
|
677
|
+
if (
|
|
678
|
+
typeof worksheet?.id !== "string" ||
|
|
679
|
+
!Number.isInteger(address?.row) ||
|
|
680
|
+
!Number.isInteger(address?.col) ||
|
|
681
|
+
!Number.isInteger(address?.rowCount) ||
|
|
682
|
+
!Number.isInteger(address?.colCount)
|
|
683
|
+
) {
|
|
684
|
+
throw new Error("Cannot locate spreadsheet range");
|
|
685
|
+
}
|
|
686
|
+
const row = address.row as number;
|
|
687
|
+
const col = address.col as number;
|
|
688
|
+
const rowCount = address.rowCount as number;
|
|
689
|
+
const colCount = address.colCount as number;
|
|
690
|
+
const sheetId = worksheet.id;
|
|
691
|
+
return {
|
|
692
|
+
key: `range:${sheetId}:${row}:${col}:${rowCount}:${colCount}`,
|
|
693
|
+
resolve(root) {
|
|
694
|
+
const workbook = root as unknown as { resolve?: (id: string) => unknown };
|
|
695
|
+
const sheet = workbook.resolve?.(sheetId) as {
|
|
696
|
+
getRangeByIndexes?: (row: number, col: number, rows: number, columns: number) => unknown;
|
|
697
|
+
};
|
|
698
|
+
if (!sheet || typeof sheet.getRangeByIndexes !== "function") {
|
|
699
|
+
throw new Error(`Worksheet no longer exists: ${sheetId}`);
|
|
700
|
+
}
|
|
701
|
+
return sheet.getRangeByIndexes(row, col, rowCount, colCount);
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function isRangeLike(value: object): boolean {
|
|
707
|
+
const address = Reflect.get(value, "address");
|
|
708
|
+
const worksheet = Reflect.get(value, "worksheet");
|
|
709
|
+
return (
|
|
710
|
+
isObject(address) && isObject(worksheet) && typeof Reflect.get(worksheet, "id") === "string"
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function isProxyCandidate(value: unknown): value is object {
|
|
715
|
+
if (!isObject(value)) return false;
|
|
716
|
+
if (
|
|
717
|
+
value instanceof Date ||
|
|
718
|
+
value instanceof RegExp ||
|
|
719
|
+
value instanceof ArrayBuffer ||
|
|
720
|
+
ArrayBuffer.isView(value) ||
|
|
721
|
+
value instanceof Blob ||
|
|
722
|
+
value instanceof Map ||
|
|
723
|
+
value instanceof Set ||
|
|
724
|
+
value instanceof Promise ||
|
|
725
|
+
Object.isFrozen(value)
|
|
726
|
+
) {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function cloneProjectionGraph<Root extends object>(root: Root): Root {
|
|
733
|
+
return cloneGraph(root, new Map()) as Root;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function cloneGraph(value: unknown, seen: Map<object, unknown>): unknown {
|
|
737
|
+
if (!isObject(value) || typeof value === "function") return value;
|
|
738
|
+
const known = seen.get(value);
|
|
739
|
+
if (known !== undefined) return known;
|
|
740
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
741
|
+
if (value instanceof RegExp) return new RegExp(value.source, value.flags);
|
|
742
|
+
if (value instanceof ArrayBuffer) return value.slice(0);
|
|
743
|
+
if (ArrayBuffer.isView(value)) {
|
|
744
|
+
if (value instanceof DataView) {
|
|
745
|
+
return new DataView(
|
|
746
|
+
value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength),
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
const constructor = value.constructor as new (input: ArrayLike<number>) => ArrayBufferView;
|
|
750
|
+
return new constructor(value as unknown as ArrayLike<number>);
|
|
751
|
+
}
|
|
752
|
+
if (value instanceof Blob) return value.slice(0, value.size, value.type);
|
|
753
|
+
if (value instanceof Map) {
|
|
754
|
+
const output = new Map();
|
|
755
|
+
seen.set(value, output);
|
|
756
|
+
for (const [key, entry] of value) output.set(cloneGraph(key, seen), cloneGraph(entry, seen));
|
|
757
|
+
return output;
|
|
758
|
+
}
|
|
759
|
+
if (value instanceof Set) {
|
|
760
|
+
const output = new Set();
|
|
761
|
+
seen.set(value, output);
|
|
762
|
+
for (const entry of value) output.add(cloneGraph(entry, seen));
|
|
763
|
+
return output;
|
|
764
|
+
}
|
|
765
|
+
if (value instanceof WeakMap || value instanceof WeakSet || value instanceof Promise)
|
|
766
|
+
return value;
|
|
767
|
+
|
|
768
|
+
const output: object = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
|
|
769
|
+
seen.set(value, output);
|
|
770
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
771
|
+
if (Array.isArray(value) && key === "length") continue;
|
|
772
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
|
|
773
|
+
if (!descriptor) continue;
|
|
774
|
+
if ("value" in descriptor) descriptor.value = cloneGraph(descriptor.value, seen);
|
|
775
|
+
Reflect.defineProperty(output, key, descriptor);
|
|
776
|
+
}
|
|
777
|
+
if (Object.isFrozen(value)) Object.freeze(output);
|
|
778
|
+
else if (Object.isSealed(value)) Object.seal(output);
|
|
779
|
+
else if (!Object.isExtensible(value)) Object.preventExtensions(output);
|
|
780
|
+
return output;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function safeRevision(value: bigint): number {
|
|
784
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
785
|
+
throw new RangeError("Native artifact revision exceeds the JavaScript safe-integer range");
|
|
786
|
+
}
|
|
787
|
+
return Number(value);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function propertyKey(value: PropertyKey): string {
|
|
791
|
+
return typeof value === "symbol" ? `symbol:${value.description ?? ""}` : String(value);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function isPromiseLike<Result>(value: Result): value is Result & Promise<Awaited<Result>> {
|
|
795
|
+
return isObject(value) && typeof Reflect.get(value, "then") === "function";
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function isObject(value: unknown): value is object {
|
|
799
|
+
return (typeof value === "object" && value !== null) || typeof value === "function";
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function isPlainObject(value: object): boolean {
|
|
803
|
+
const prototype = Object.getPrototypeOf(value);
|
|
804
|
+
return prototype === Object.prototype || prototype === null;
|
|
805
|
+
}
|