@highstate/contract 0.19.1 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/compaction.ts DELETED
@@ -1,381 +0,0 @@
1
- import { mapValues } from "remeda"
2
- import { HighstateSignature, objectRefSchema, objectWithIdSchema } from "./instance"
3
-
4
- export function compact<T>(value: T): unknown {
5
- const counts = new WeakMap<object, number>()
6
- const cyclic = new WeakSet<object>()
7
- const expanded = new WeakSet<object>()
8
- const inStack = new WeakSet<object>()
9
- const stack: object[] = []
10
-
11
- function countIdentities(current: unknown): void {
12
- if (current === null || typeof current !== "object") {
13
- return
14
- }
15
-
16
- counts.set(current, (counts.get(current) ?? 0) + 1)
17
-
18
- if (inStack.has(current)) {
19
- cyclic.add(current)
20
-
21
- for (const entry of stack) {
22
- cyclic.add(entry)
23
- }
24
- return
25
- }
26
-
27
- if (expanded.has(current)) {
28
- return
29
- }
30
-
31
- expanded.add(current)
32
- inStack.add(current)
33
- stack.push(current)
34
-
35
- if (Array.isArray(current)) {
36
- try {
37
- for (const item of current) {
38
- countIdentities(item)
39
- }
40
- return
41
- } finally {
42
- stack.pop()
43
- inStack.delete(current)
44
- }
45
- }
46
-
47
- try {
48
- for (const entryValue of Object.values(current)) {
49
- countIdentities(entryValue)
50
- }
51
- } finally {
52
- stack.pop()
53
- inStack.delete(current)
54
- }
55
- }
56
-
57
- countIdentities(value)
58
-
59
- type DefinitionSite = { parent: object; key: string | number }
60
-
61
- const ids = new WeakMap<object, number>()
62
- const definitionSites = new WeakMap<object, DefinitionSite>()
63
- let nextId = 1
64
-
65
- function ensureId(current: object): number {
66
- const existing = ids.get(current)
67
- if (existing !== undefined) {
68
- return existing
69
- }
70
-
71
- const allocated = nextId
72
- nextId += 1
73
- ids.set(current, allocated)
74
- return allocated
75
- }
76
-
77
- function assignDefinitionSitesBfs(root: unknown): void {
78
- if (root === null || typeof root !== "object") {
79
- return
80
- }
81
-
82
- const queue: Array<{ parent: object; key: string | number; value: unknown }> = []
83
- const expandedQueue = new WeakSet<object>()
84
-
85
- if (Array.isArray(root)) {
86
- for (let index = 0; index < root.length; index += 1) {
87
- queue.push({ parent: root, key: index, value: root[index] })
88
- }
89
- } else {
90
- for (const [key, entryValue] of Object.entries(root)) {
91
- queue.push({ parent: root, key, value: entryValue })
92
- }
93
- }
94
-
95
- while (queue.length > 0) {
96
- const current = queue.shift()
97
- if (current === undefined) {
98
- continue
99
- }
100
-
101
- if (current.value === null || typeof current.value !== "object") {
102
- continue
103
- }
104
-
105
- const occurrences = counts.get(current.value) ?? 0
106
- if (occurrences > 1 && definitionSites.get(current.value) === undefined) {
107
- definitionSites.set(current.value, { parent: current.parent, key: current.key })
108
- ensureId(current.value)
109
- }
110
-
111
- if (expandedQueue.has(current.value)) {
112
- continue
113
- }
114
-
115
- expandedQueue.add(current.value)
116
-
117
- if (Array.isArray(current.value)) {
118
- for (let index = 0; index < current.value.length; index += 1) {
119
- queue.push({ parent: current.value, key: index, value: current.value[index] })
120
- }
121
- continue
122
- }
123
-
124
- for (const [key, entryValue] of Object.entries(current.value)) {
125
- queue.push({ parent: current.value, key, value: entryValue })
126
- }
127
- }
128
- }
129
-
130
- assignDefinitionSitesBfs(value)
131
-
132
- const emitted = new WeakSet<object>()
133
-
134
- function buildTreeChildren(current: object, rootOfIdValue?: object): unknown {
135
- if (Array.isArray(current)) {
136
- return current.map(item => buildTree(item, rootOfIdValue))
137
- }
138
-
139
- return mapValues(current, entryValue => buildTree(entryValue, rootOfIdValue))
140
- }
141
-
142
- function buildTree(current: unknown, rootOfIdValue?: object): unknown {
143
- if (current === null || typeof current !== "object") {
144
- return current
145
- }
146
-
147
- // Inside an Id value, never emit nested Id wrappers.
148
- // Match existing real-world snapshot: do not introduce refs for nested objects
149
- // unless the nested object has already been defined earlier.
150
- if (rootOfIdValue !== undefined && emitted.has(current)) {
151
- const id = ids.get(current)
152
- if (id === undefined) {
153
- throw new Error("Compaction invariant violation: missing id for repeated object")
154
- }
155
-
156
- return {
157
- [HighstateSignature.Ref]: true,
158
- id,
159
- }
160
- }
161
-
162
- // Cycles inside an Id value require defining the cyclic objects inline,
163
- // otherwise we would infinitely recurse.
164
- if (rootOfIdValue !== undefined && cyclic.has(current) && !emitted.has(current)) {
165
- const id = ensureId(current)
166
- emitted.add(current)
167
-
168
- return {
169
- [HighstateSignature.Id]: true,
170
- id,
171
- value: buildTreeChildren(current, current),
172
- }
173
- }
174
-
175
- return buildTreeChildren(current, rootOfIdValue)
176
- }
177
-
178
- function buildAt(parent: object, key: string | number, current: unknown): unknown {
179
- if (current === null || typeof current !== "object") {
180
- return current
181
- }
182
-
183
- const occurrences = counts.get(current) ?? 0
184
- if (occurrences <= 1) {
185
- if (Array.isArray(current)) {
186
- return current.map((item, index) => buildAt(current, index, item))
187
- }
188
-
189
- return mapValues(current, (entryValue, entryKey) => buildAt(current, entryKey, entryValue))
190
- }
191
-
192
- const site = definitionSites.get(current)
193
- const shouldDefineHere = site?.parent === parent && site.key === key
194
-
195
- const id = ids.get(current)
196
- if (id === undefined) {
197
- throw new Error("Compaction invariant violation: missing id for repeated object")
198
- }
199
-
200
- if (!shouldDefineHere || emitted.has(current)) {
201
- return {
202
- [HighstateSignature.Ref]: true,
203
- id,
204
- }
205
- }
206
-
207
- emitted.add(current)
208
-
209
- return {
210
- [HighstateSignature.Id]: true,
211
- id,
212
- value: buildTreeChildren(current, current),
213
- }
214
- }
215
-
216
- // Prefer keeping the top-level value unwrapped for stability.
217
- if (value === null || typeof value !== "object") {
218
- return value
219
- }
220
-
221
- const topLevelOccurrences = counts.get(value) ?? 0
222
- if (topLevelOccurrences > 1) {
223
- const id = ensureId(value)
224
- emitted.add(value)
225
-
226
- return {
227
- [HighstateSignature.Id]: true,
228
- id,
229
- value: buildTreeChildren(value, value),
230
- }
231
- }
232
-
233
- if (Array.isArray(value)) {
234
- return value.map((item, index) => buildAt(value, index, item))
235
- }
236
-
237
- return mapValues(value as Record<string, unknown>, (entryValue, entryKey) =>
238
- buildAt(value as Record<string, unknown>, entryKey, entryValue),
239
- )
240
- }
241
-
242
- export function decompact<T>(value: unknown): T {
243
- const rawValuesById = new Map<number, unknown>()
244
- const placeholdersById = new Map<number, unknown>()
245
-
246
- function collect(current: unknown, visited: WeakSet<object>): void {
247
- const result = objectWithIdSchema.safeParse(current)
248
- if (result.success) {
249
- const { id } = result.data
250
- if (rawValuesById.has(id)) {
251
- throw new Error(`Duplicate compacted id ${id}`)
252
- }
253
-
254
- rawValuesById.set(id, result.data.value)
255
- collect(result.data.value, visited)
256
- return
257
- }
258
-
259
- if (current === null || current === undefined || typeof current !== "object") {
260
- return
261
- }
262
-
263
- if (visited.has(current)) {
264
- return
265
- }
266
-
267
- visited.add(current)
268
-
269
- if (Array.isArray(current)) {
270
- for (const item of current) {
271
- collect(item, visited)
272
- }
273
- return
274
- }
275
-
276
- for (const entryValue of Object.values(current)) {
277
- collect(entryValue, visited)
278
- }
279
- }
280
-
281
- function ensurePlaceholder(id: number): unknown {
282
- const existing = placeholdersById.get(id)
283
- if (existing !== undefined) {
284
- return existing
285
- }
286
-
287
- const raw = rawValuesById.get(id)
288
- if (raw === undefined) {
289
- throw new Error(`Unresolved compacted ref id ${id}`)
290
- }
291
-
292
- let placeholder: unknown
293
- if (raw !== null && typeof raw === "object") {
294
- placeholder = Array.isArray(raw) ? [] : {}
295
- } else {
296
- placeholder = raw
297
- }
298
-
299
- placeholdersById.set(id, placeholder)
300
- return placeholder
301
- }
302
-
303
- function resolve(current: unknown): unknown {
304
- const refResult = objectRefSchema.safeParse(current)
305
- if (refResult.success) {
306
- return ensurePlaceholder(refResult.data.id)
307
- }
308
-
309
- const withIdResult = objectWithIdSchema.safeParse(current)
310
- if (withIdResult.success) {
311
- return ensurePlaceholder(withIdResult.data.id)
312
- }
313
-
314
- if (current === null || current === undefined || typeof current !== "object") {
315
- return current
316
- }
317
-
318
- if (Array.isArray(current)) {
319
- return current.map(item => resolve(item))
320
- }
321
-
322
- return mapValues(current, entryValue => resolve(entryValue))
323
- }
324
-
325
- function fillPlaceholder(id: number, visitedIds: Set<number>): void {
326
- if (visitedIds.has(id)) {
327
- return
328
- }
329
-
330
- visitedIds.add(id)
331
-
332
- const raw = rawValuesById.get(id)
333
- if (raw === undefined) {
334
- throw new Error(`Unresolved compacted ref id ${id}`)
335
- }
336
-
337
- const placeholder = ensurePlaceholder(id)
338
- if (placeholder === null || typeof placeholder !== "object") {
339
- return
340
- }
341
-
342
- if (raw === null || typeof raw !== "object") {
343
- throw new Error(`Compaction invariant violation: id ${id} points to non-object value`)
344
- }
345
-
346
- const resolved = resolve(raw)
347
-
348
- if (Array.isArray(placeholder)) {
349
- if (!Array.isArray(resolved)) {
350
- throw new Error(`Compaction invariant violation: id ${id} array placeholder mismatch`)
351
- }
352
-
353
- placeholder.length = 0
354
- for (const item of resolved) {
355
- placeholder.push(item)
356
- }
357
- return
358
- }
359
-
360
- if (Array.isArray(resolved) || resolved === null || typeof resolved !== "object") {
361
- throw new Error(`Compaction invariant violation: id ${id} object placeholder mismatch`)
362
- }
363
-
364
- for (const [key, entryValue] of Object.entries(resolved)) {
365
- ;(placeholder as Record<string, unknown>)[key] = entryValue
366
- }
367
- }
368
-
369
- collect(value, new WeakSet<object>())
370
-
371
- for (const id of rawValuesById.keys()) {
372
- ensurePlaceholder(id)
373
- }
374
-
375
- const visitedIds = new Set<number>()
376
- for (const id of rawValuesById.keys()) {
377
- fillPlaceholder(id, visitedIds)
378
- }
379
-
380
- return resolve(value) as T
381
- }