@ifc-lite/collab 0.2.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 (219) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +92 -0
  3. package/dist/awareness/agent.d.ts +36 -0
  4. package/dist/awareness/agent.d.ts.map +1 -0
  5. package/dist/awareness/agent.js +39 -0
  6. package/dist/awareness/agent.js.map +1 -0
  7. package/dist/awareness/color.d.ts +31 -0
  8. package/dist/awareness/color.d.ts.map +1 -0
  9. package/dist/awareness/color.js +61 -0
  10. package/dist/awareness/color.js.map +1 -0
  11. package/dist/awareness/index.d.ts +6 -0
  12. package/dist/awareness/index.d.ts.map +1 -0
  13. package/dist/awareness/index.js +9 -0
  14. package/dist/awareness/index.js.map +1 -0
  15. package/dist/awareness/overlay.d.ts +59 -0
  16. package/dist/awareness/overlay.d.ts.map +1 -0
  17. package/dist/awareness/overlay.js +110 -0
  18. package/dist/awareness/overlay.js.map +1 -0
  19. package/dist/awareness/presence.d.ts +95 -0
  20. package/dist/awareness/presence.d.ts.map +1 -0
  21. package/dist/awareness/presence.js +114 -0
  22. package/dist/awareness/presence.js.map +1 -0
  23. package/dist/awareness/render.d.ts +58 -0
  24. package/dist/awareness/render.d.ts.map +1 -0
  25. package/dist/awareness/render.js +66 -0
  26. package/dist/awareness/render.js.map +1 -0
  27. package/dist/branch/branch-tree.d.ts +40 -0
  28. package/dist/branch/branch-tree.d.ts.map +1 -0
  29. package/dist/branch/branch-tree.js +66 -0
  30. package/dist/branch/branch-tree.js.map +1 -0
  31. package/dist/branch/branch.d.ts +44 -0
  32. package/dist/branch/branch.d.ts.map +1 -0
  33. package/dist/branch/branch.js +109 -0
  34. package/dist/branch/branch.js.map +1 -0
  35. package/dist/branch/history-automerge.d.ts +31 -0
  36. package/dist/branch/history-automerge.d.ts.map +1 -0
  37. package/dist/branch/history-automerge.js +237 -0
  38. package/dist/branch/history-automerge.js.map +1 -0
  39. package/dist/branch/history.d.ts +147 -0
  40. package/dist/branch/history.d.ts.map +1 -0
  41. package/dist/branch/history.js +223 -0
  42. package/dist/branch/history.js.map +1 -0
  43. package/dist/branch/index.d.ts +5 -0
  44. package/dist/branch/index.d.ts.map +1 -0
  45. package/dist/branch/index.js +8 -0
  46. package/dist/branch/index.js.map +1 -0
  47. package/dist/conflicts/detector.d.ts +52 -0
  48. package/dist/conflicts/detector.d.ts.map +1 -0
  49. package/dist/conflicts/detector.js +226 -0
  50. package/dist/conflicts/detector.js.map +1 -0
  51. package/dist/conflicts/index.d.ts +3 -0
  52. package/dist/conflicts/index.d.ts.map +1 -0
  53. package/dist/conflicts/index.js +6 -0
  54. package/dist/conflicts/index.js.map +1 -0
  55. package/dist/conflicts/ui-bridge.d.ts +80 -0
  56. package/dist/conflicts/ui-bridge.d.ts.map +1 -0
  57. package/dist/conflicts/ui-bridge.js +126 -0
  58. package/dist/conflicts/ui-bridge.js.map +1 -0
  59. package/dist/doc/entity.d.ts +84 -0
  60. package/dist/doc/entity.d.ts.map +1 -0
  61. package/dist/doc/entity.js +345 -0
  62. package/dist/doc/entity.js.map +1 -0
  63. package/dist/doc/geometry.d.ts +39 -0
  64. package/dist/doc/geometry.d.ts.map +1 -0
  65. package/dist/doc/geometry.js +99 -0
  66. package/dist/doc/geometry.js.map +1 -0
  67. package/dist/doc/index.d.ts +8 -0
  68. package/dist/doc/index.d.ts.map +1 -0
  69. package/dist/doc/index.js +11 -0
  70. package/dist/doc/index.js.map +1 -0
  71. package/dist/doc/migration-ifc4-to-ifc4x3.d.ts +21 -0
  72. package/dist/doc/migration-ifc4-to-ifc4x3.d.ts.map +1 -0
  73. package/dist/doc/migration-ifc4-to-ifc4x3.js +55 -0
  74. package/dist/doc/migration-ifc4-to-ifc4x3.js.map +1 -0
  75. package/dist/doc/relationship.d.ts +27 -0
  76. package/dist/doc/relationship.d.ts.map +1 -0
  77. package/dist/doc/relationship.js +110 -0
  78. package/dist/doc/relationship.js.map +1 -0
  79. package/dist/doc/schema-version.d.ts +47 -0
  80. package/dist/doc/schema-version.d.ts.map +1 -0
  81. package/dist/doc/schema-version.js +41 -0
  82. package/dist/doc/schema-version.js.map +1 -0
  83. package/dist/doc/schema.d.ts +131 -0
  84. package/dist/doc/schema.d.ts.map +1 -0
  85. package/dist/doc/schema.js +117 -0
  86. package/dist/doc/schema.js.map +1 -0
  87. package/dist/doc/units.d.ts +45 -0
  88. package/dist/doc/units.d.ts.map +1 -0
  89. package/dist/doc/units.js +120 -0
  90. package/dist/doc/units.js.map +1 -0
  91. package/dist/federation/bridge.d.ts +34 -0
  92. package/dist/federation/bridge.d.ts.map +1 -0
  93. package/dist/federation/bridge.js +52 -0
  94. package/dist/federation/bridge.js.map +1 -0
  95. package/dist/federation/index.d.ts +4 -0
  96. package/dist/federation/index.d.ts.map +1 -0
  97. package/dist/federation/index.js +7 -0
  98. package/dist/federation/index.js.map +1 -0
  99. package/dist/federation/resolver.d.ts +58 -0
  100. package/dist/federation/resolver.d.ts.map +1 -0
  101. package/dist/federation/resolver.js +43 -0
  102. package/dist/federation/resolver.js.map +1 -0
  103. package/dist/federation/session.d.ts +79 -0
  104. package/dist/federation/session.d.ts.map +1 -0
  105. package/dist/federation/session.js +126 -0
  106. package/dist/federation/session.js.map +1 -0
  107. package/dist/geometry/blob-store.d.ts +106 -0
  108. package/dist/geometry/blob-store.d.ts.map +1 -0
  109. package/dist/geometry/blob-store.js +266 -0
  110. package/dist/geometry/blob-store.js.map +1 -0
  111. package/dist/geometry/csg.d.ts +63 -0
  112. package/dist/geometry/csg.d.ts.map +1 -0
  113. package/dist/geometry/csg.js +101 -0
  114. package/dist/geometry/csg.js.map +1 -0
  115. package/dist/geometry/determinism.d.ts +45 -0
  116. package/dist/geometry/determinism.d.ts.map +1 -0
  117. package/dist/geometry/determinism.js +92 -0
  118. package/dist/geometry/determinism.js.map +1 -0
  119. package/dist/geometry/gc.d.ts +63 -0
  120. package/dist/geometry/gc.d.ts.map +1 -0
  121. package/dist/geometry/gc.js +109 -0
  122. package/dist/geometry/gc.js.map +1 -0
  123. package/dist/geometry/index.d.ts +6 -0
  124. package/dist/geometry/index.d.ts.map +1 -0
  125. package/dist/geometry/index.js +9 -0
  126. package/dist/geometry/index.js.map +1 -0
  127. package/dist/geometry/parametric.d.ts +92 -0
  128. package/dist/geometry/parametric.d.ts.map +1 -0
  129. package/dist/geometry/parametric.js +200 -0
  130. package/dist/geometry/parametric.js.map +1 -0
  131. package/dist/index.d.ts +27 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +46 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/mutations/bind.d.ts +56 -0
  136. package/dist/mutations/bind.d.ts.map +1 -0
  137. package/dist/mutations/bind.js +68 -0
  138. package/dist/mutations/bind.js.map +1 -0
  139. package/dist/mutations/index.d.ts +2 -0
  140. package/dist/mutations/index.d.ts.map +1 -0
  141. package/dist/mutations/index.js +5 -0
  142. package/dist/mutations/index.js.map +1 -0
  143. package/dist/perf/benchmark.d.ts +25 -0
  144. package/dist/perf/benchmark.d.ts.map +1 -0
  145. package/dist/perf/benchmark.js +97 -0
  146. package/dist/perf/benchmark.js.map +1 -0
  147. package/dist/perf/index.d.ts +3 -0
  148. package/dist/perf/index.d.ts.map +1 -0
  149. package/dist/perf/index.js +6 -0
  150. package/dist/perf/index.js.map +1 -0
  151. package/dist/perf/latency.d.ts +39 -0
  152. package/dist/perf/latency.d.ts.map +1 -0
  153. package/dist/perf/latency.js +79 -0
  154. package/dist/perf/latency.js.map +1 -0
  155. package/dist/privacy.d.ts +45 -0
  156. package/dist/privacy.d.ts.map +1 -0
  157. package/dist/privacy.js +66 -0
  158. package/dist/privacy.js.map +1 -0
  159. package/dist/providers/indexeddb.d.ts +37 -0
  160. package/dist/providers/indexeddb.d.ts.map +1 -0
  161. package/dist/providers/indexeddb.js +45 -0
  162. package/dist/providers/indexeddb.js.map +1 -0
  163. package/dist/providers/webrtc.d.ts +40 -0
  164. package/dist/providers/webrtc.d.ts.map +1 -0
  165. package/dist/providers/webrtc.js +81 -0
  166. package/dist/providers/webrtc.js.map +1 -0
  167. package/dist/providers/websocket.d.ts +45 -0
  168. package/dist/providers/websocket.d.ts.map +1 -0
  169. package/dist/providers/websocket.js +56 -0
  170. package/dist/providers/websocket.js.map +1 -0
  171. package/dist/security/e2e.d.ts +54 -0
  172. package/dist/security/e2e.d.ts.map +1 -0
  173. package/dist/security/e2e.js +147 -0
  174. package/dist/security/e2e.js.map +1 -0
  175. package/dist/security/index.d.ts +2 -0
  176. package/dist/security/index.d.ts.map +1 -0
  177. package/dist/security/index.js +5 -0
  178. package/dist/security/index.js.map +1 -0
  179. package/dist/session.d.ts +79 -0
  180. package/dist/session.d.ts.map +1 -0
  181. package/dist/session.js +112 -0
  182. package/dist/session.js.map +1 -0
  183. package/dist/snapshot/from-ifcx.d.ts +24 -0
  184. package/dist/snapshot/from-ifcx.d.ts.map +1 -0
  185. package/dist/snapshot/from-ifcx.js +93 -0
  186. package/dist/snapshot/from-ifcx.js.map +1 -0
  187. package/dist/snapshot/index.d.ts +6 -0
  188. package/dist/snapshot/index.d.ts.map +1 -0
  189. package/dist/snapshot/index.js +9 -0
  190. package/dist/snapshot/index.js.map +1 -0
  191. package/dist/snapshot/layers.d.ts +56 -0
  192. package/dist/snapshot/layers.d.ts.map +1 -0
  193. package/dist/snapshot/layers.js +82 -0
  194. package/dist/snapshot/layers.js.map +1 -0
  195. package/dist/snapshot/minimal-layer.d.ts +40 -0
  196. package/dist/snapshot/minimal-layer.d.ts.map +1 -0
  197. package/dist/snapshot/minimal-layer.js +123 -0
  198. package/dist/snapshot/minimal-layer.js.map +1 -0
  199. package/dist/snapshot/to-ifcx.d.ts +26 -0
  200. package/dist/snapshot/to-ifcx.d.ts.map +1 -0
  201. package/dist/snapshot/to-ifcx.js +54 -0
  202. package/dist/snapshot/to-ifcx.js.map +1 -0
  203. package/dist/snapshot/worker.d.ts +45 -0
  204. package/dist/snapshot/worker.d.ts.map +1 -0
  205. package/dist/snapshot/worker.js +73 -0
  206. package/dist/snapshot/worker.js.map +1 -0
  207. package/dist/sync/room.d.ts +15 -0
  208. package/dist/sync/room.d.ts.map +1 -0
  209. package/dist/sync/room.js +20 -0
  210. package/dist/sync/room.js.map +1 -0
  211. package/dist/undo.d.ts +25 -0
  212. package/dist/undo.d.ts.map +1 -0
  213. package/dist/undo.js +37 -0
  214. package/dist/undo.js.map +1 -0
  215. package/dist/viewer-bridge.d.ts +27 -0
  216. package/dist/viewer-bridge.d.ts.map +1 -0
  217. package/dist/viewer-bridge.js +82 -0
  218. package/dist/viewer-bridge.js.map +1 -0
  219. package/package.json +83 -0
@@ -0,0 +1,101 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * CSG (constructive solid geometry) operation tree as a Y.Array<CSGOp>.
6
+ *
7
+ * Spec §9.4 / §11 + open problem #4: full CRDT-tree merging of CSG ops is
8
+ * a v1.x topic. v0.1 stores ops as an ordered Y.Array — concurrent
9
+ * additions from two peers interleave preserving each peer's relative
10
+ * order, and a UI can drag-to-reorder via explicit moves. The CSG result
11
+ * is order-dependent; that's exposed honestly in the README.
12
+ *
13
+ * The geometry node referenced here is one of the Y.Doc's
14
+ * `geometry` top-level entries with `type: 'csg-tree'`. The tree's ops
15
+ * live as a Y.Array on `params.ops` so they ride the same CRDT machinery
16
+ * as parametric params.
17
+ */
18
+ import * as Y from 'yjs';
19
+ import { GEOMETRY_KEY, geometryMap } from '../doc/schema.js';
20
+ import { createGeometry } from '../doc/geometry.js';
21
+ /**
22
+ * Get-or-create a CSG-tree geometry node and return its `ops` Y.Array.
23
+ *
24
+ * Idempotent: if the geometry already exists with `type==='csg-tree'`,
25
+ * returns the existing ops Y.Array.
26
+ */
27
+ export function ensureCSGTree(doc, geomId, options = {}) {
28
+ let node = geometryMap(doc).get(geomId);
29
+ if (!node) {
30
+ node = createGeometry(doc, geomId, {
31
+ type: 'csg-tree',
32
+ source: 'csg-op',
33
+ bbox: options.bbox,
34
+ });
35
+ }
36
+ const params = node.get(GEOMETRY_KEY.PARAMS);
37
+ if (!params)
38
+ throw new Error(`@ifc-lite/collab: csg-tree "${geomId}" missing params map`);
39
+ let ops = params.get('ops');
40
+ if (!ops) {
41
+ ops = new Y.Array();
42
+ params.set('ops', ops);
43
+ }
44
+ if (options.ops && ops.length === 0)
45
+ ops.push(options.ops);
46
+ return ops;
47
+ }
48
+ export function getCSGOps(doc, geomId) {
49
+ const node = geometryMap(doc).get(geomId);
50
+ const params = node?.get(GEOMETRY_KEY.PARAMS);
51
+ const ops = params?.get('ops');
52
+ return ops ? ops.toArray() : [];
53
+ }
54
+ /** Append one CSG op. Concurrent appends interleave per Y.Array semantics. */
55
+ export function appendCSGOp(doc, geomId, op) {
56
+ const ops = ensureCSGTree(doc, geomId);
57
+ ops.push([op]);
58
+ return ops.length - 1;
59
+ }
60
+ /** Insert a CSG op at a specific index. */
61
+ export function insertCSGOp(doc, geomId, index, op) {
62
+ const ops = ensureCSGTree(doc, geomId);
63
+ ops.insert(Math.max(0, Math.min(index, ops.length)), [op]);
64
+ }
65
+ /**
66
+ * Remove a CSG op by `opId`. Returns true if found and removed.
67
+ *
68
+ * We search by opId rather than index so concurrent inserts elsewhere
69
+ * don't shift the target.
70
+ */
71
+ export function removeCSGOp(doc, geomId, opId) {
72
+ const ops = ensureCSGTree(doc, geomId);
73
+ const arr = ops.toArray();
74
+ for (let i = arr.length - 1; i >= 0; i--) {
75
+ if (arr[i].opId === opId) {
76
+ ops.delete(i, 1);
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ /**
83
+ * Reorder a CSG op (move by opId to a new index). Implemented as
84
+ * delete-then-insert; the underlying CRDT semantics keep concurrent
85
+ * reorder + reorder visible as the last-applied wins (ordering is what
86
+ * the user is *for sure* fighting over).
87
+ */
88
+ export function moveCSGOp(doc, geomId, opId, toIndex) {
89
+ const ops = ensureCSGTree(doc, geomId);
90
+ const arr = ops.toArray();
91
+ const idx = arr.findIndex((o) => o.opId === opId);
92
+ if (idx < 0)
93
+ return false;
94
+ const [op] = arr.splice(idx, 1);
95
+ doc.transact(() => {
96
+ ops.delete(idx, 1);
97
+ ops.insert(Math.max(0, Math.min(toIndex, ops.length)), [op]);
98
+ });
99
+ return true;
100
+ }
101
+ //# sourceMappingURL=csg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csg.js","sourceRoot":"","sources":["../../src/geometry/csg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Determinism harness (spec open problem #5).
3
+ *
4
+ * The CRDT pipeline assumes the geometry kernel is bit-identical
5
+ * across machines for the same params. This harness lets a CI matrix
6
+ * run the kernel against a fixed set of param fixtures and compare
7
+ * hashes. Drift is reported, not silently masked.
8
+ *
9
+ * Designed to be runtime-agnostic: pass a `kernel(params) => Mesh`
10
+ * callable and a list of fixtures.
11
+ */
12
+ import { type Mesh, type ParametricSource } from './parametric.js';
13
+ export interface DeterminismFixture {
14
+ name: string;
15
+ source: ParametricSource;
16
+ params: unknown;
17
+ }
18
+ export interface DeterminismResult {
19
+ name: string;
20
+ hash: string;
21
+ ok: boolean;
22
+ /** Expected hash, when comparing against a known-good baseline. */
23
+ expected?: string;
24
+ }
25
+ export interface DeterminismReport {
26
+ results: DeterminismResult[];
27
+ ok: boolean;
28
+ }
29
+ export type Kernel = (source: ParametricSource, params: unknown) => Mesh;
30
+ /**
31
+ * Run `kernel` against every fixture and return per-fixture hashes.
32
+ *
33
+ * If `expected` is supplied, *every* fixture must have a matching entry
34
+ * — a missing entry counts as a failure, not an implicit pass. This is
35
+ * intentional: a typo in a fixture name or a forgotten baseline update
36
+ * used to silently report `ok=true`, which made drift unobservable. If
37
+ * you genuinely want partial baselines, omit `expected` entirely.
38
+ */
39
+ export declare function runDeterminismHarness(kernel: Kernel, fixtures: readonly DeterminismFixture[], expected?: Record<string, string>): DeterminismReport;
40
+ /**
41
+ * Default fixture set covering all built-in primitives.
42
+ * Apps extend with their own per-platform-stable parametric library.
43
+ */
44
+ export declare const DEFAULT_FIXTURES: readonly DeterminismFixture[];
45
+ //# sourceMappingURL=determinism.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"determinism.d.ts","sourceRoot":"","sources":["../../src/geometry/determinism.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAY,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE7E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAEzE;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,iBAAiB,CAsBnB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,kBAAkB,EAoCzD,CAAC"}
@@ -0,0 +1,92 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Determinism harness (spec open problem #5).
6
+ *
7
+ * The CRDT pipeline assumes the geometry kernel is bit-identical
8
+ * across machines for the same params. This harness lets a CI matrix
9
+ * run the kernel against a fixed set of param fixtures and compare
10
+ * hashes. Drift is reported, not silently masked.
11
+ *
12
+ * Designed to be runtime-agnostic: pass a `kernel(params) => Mesh`
13
+ * callable and a list of fixtures.
14
+ */
15
+ import { hashMesh } from './parametric.js';
16
+ /**
17
+ * Run `kernel` against every fixture and return per-fixture hashes.
18
+ *
19
+ * If `expected` is supplied, *every* fixture must have a matching entry
20
+ * — a missing entry counts as a failure, not an implicit pass. This is
21
+ * intentional: a typo in a fixture name or a forgotten baseline update
22
+ * used to silently report `ok=true`, which made drift unobservable. If
23
+ * you genuinely want partial baselines, omit `expected` entirely.
24
+ */
25
+ export function runDeterminismHarness(kernel, fixtures, expected) {
26
+ const results = [];
27
+ let allOk = true;
28
+ const baselineProvided = expected !== undefined;
29
+ for (const fixture of fixtures) {
30
+ const mesh = kernel(fixture.source, fixture.params);
31
+ const hash = hashMesh(mesh);
32
+ const exp = expected?.[fixture.name];
33
+ let ok;
34
+ if (!baselineProvided) {
35
+ ok = true;
36
+ }
37
+ else if (exp == null) {
38
+ // Baseline was provided but doesn't cover this fixture — refuse
39
+ // to silently report success.
40
+ ok = false;
41
+ }
42
+ else {
43
+ ok = hash === exp;
44
+ }
45
+ if (!ok)
46
+ allOk = false;
47
+ results.push({ name: fixture.name, hash, ok, expected: exp });
48
+ }
49
+ return { results, ok: allOk };
50
+ }
51
+ /**
52
+ * Default fixture set covering all built-in primitives.
53
+ * Apps extend with their own per-platform-stable parametric library.
54
+ */
55
+ export const DEFAULT_FIXTURES = [
56
+ {
57
+ name: 'box-1x2x3',
58
+ source: 'box',
59
+ params: { length: 1, width: 2, height: 3 },
60
+ },
61
+ {
62
+ name: 'cylinder-r1-h2',
63
+ source: 'cylinder',
64
+ params: { radius: 1, height: 2, segments: 16 },
65
+ },
66
+ {
67
+ name: 'extruded-rect-2x3-d4',
68
+ source: 'extruded-area-solid',
69
+ params: {
70
+ profile: { type: 'rectangle', width: 2, height: 3 },
71
+ depth: 4,
72
+ },
73
+ },
74
+ {
75
+ name: 'extruded-circle-r1-d2',
76
+ source: 'extruded-area-solid',
77
+ params: {
78
+ profile: { type: 'circle', radius: 1, segments: 16 },
79
+ depth: 2,
80
+ },
81
+ },
82
+ {
83
+ name: 'revolved-r0.5-full',
84
+ source: 'revolved-area-solid',
85
+ params: {
86
+ profile: { type: 'circle', radius: 0.5, segments: 16 },
87
+ angle: Math.PI * 2,
88
+ segments: 16,
89
+ },
90
+ },
91
+ ];
92
+ //# sourceMappingURL=determinism.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"determinism.js","sourceRoot":"","sources":["../../src/geometry/determinism.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAoC,MAAM,iBAAiB,CAAC;AAuB7E;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,QAAuC,EACvC,QAAiC;IAEjC,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,MAAM,gBAAgB,GAAG,QAAQ,KAAK,SAAS,CAAC;IAChD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,EAAW,CAAC;QAChB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,EAAE,GAAG,IAAI,CAAC;QACZ,CAAC;aAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,gEAAgE;YAChE,8BAA8B;YAC9B,EAAE,GAAG,KAAK,CAAC;QACb,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,IAAI,KAAK,GAAG,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,EAAE;YAAE,KAAK,GAAG,KAAK,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAkC;IAC7D;QACE,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;KAC3C;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,MAAM,EAAE,UAAU;QAClB,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;KAC/C;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EAAE;YACN,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;YACnD,KAAK,EAAE,CAAC;SACT;KACF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EAAE;YACN,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YACpD,KAAK,EAAE,CAAC;SACT;KACF;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,MAAM,EAAE,qBAAqB;QAC7B,MAAM,EAAE;YACN,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE;YACtD,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC;YAClB,QAAQ,EAAE,EAAE;SACb;KACF;CACF,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Blob garbage collection (open problem #6).
3
+ *
4
+ * Mesh blobs grow without bound during long sessions. This module
5
+ * implements an epoch + reference-count sweep:
6
+ *
7
+ * 1. `collectReferencedBlobHashes(doc)` walks every entity, collects
8
+ * its `geometryRef.geomId`, then resolves each geometry node to
9
+ * its `blobHash`. Returns the set of currently-referenced hashes.
10
+ *
11
+ * 2. `planBlobSweep(store, referenced, opts)` returns the list of
12
+ * blob hashes that exist in `store` but aren't referenced. With
13
+ * `epochMs` set, only blobs older than that grace window are
14
+ * candidates — that's the safety valve against deleting a blob a
15
+ * peer is about to reference but hasn't synced yet.
16
+ *
17
+ * 3. `sweepBlobs(store, decision)` deletes the candidates and reports
18
+ * bytes freed.
19
+ *
20
+ * GC is correct under sync because: each peer makes blob refs visible
21
+ * via the Y.Doc; once the Y.Doc converges across peers, the
22
+ * referenced-set is the same on every peer. The grace window covers
23
+ * the gap between "blob uploaded" and "Y.Doc reference applied."
24
+ */
25
+ import * as Y from 'yjs';
26
+ import type { BlobHash, BlobMeta, BlobStore } from './blob-store.js';
27
+ /**
28
+ * Collect every blob hash currently referenced from a Y.Doc.
29
+ *
30
+ * An entity's `geometryRef.geomId` points at a `geometry` top-level
31
+ * entry whose `blobHash` (if any) we want to keep. We also include any
32
+ * `blobHash` that appears directly in `geometry.params.<*>` so apps
33
+ * that store auxiliary blob refs in params (e.g. textures) survive
34
+ * gc.
35
+ */
36
+ export declare function collectReferencedBlobHashes(doc: Y.Doc): Set<BlobHash>;
37
+ export interface SweepOptions {
38
+ /**
39
+ * Grace window in ms — only blobs older than this are candidates.
40
+ * Default 60_000 (1 minute). Set to 0 to sweep aggressively (tests).
41
+ */
42
+ epochMs?: number;
43
+ /** Override `Date.now` for deterministic tests. */
44
+ now?: () => number;
45
+ }
46
+ export interface SweepDecision {
47
+ drop: BlobHash[];
48
+ /** Bytes that will be reclaimed once `drop` is processed. May be undefined when the store doesn't expose sizes. */
49
+ reclaimBytes: number;
50
+ }
51
+ /**
52
+ * Plan a blob sweep against `store`, retaining everything in `referenced`.
53
+ *
54
+ * The store's `list()` returns hashes only; size info comes from a
55
+ * `metaProvider` (default: walks `store.get(hash).byteLength`).
56
+ */
57
+ export declare function planBlobSweep(store: BlobStore, referenced: Set<BlobHash>, options?: SweepOptions & {
58
+ /** Optional fast-path metadata lookup; default streams via `store.get`. */
59
+ metaProvider?: (hash: BlobHash) => Promise<BlobMeta | null>;
60
+ }): Promise<SweepDecision>;
61
+ /** Apply a sweep decision: delete the candidates from `store`. */
62
+ export declare function sweepBlobs(store: BlobStore, decision: SweepDecision): Promise<number>;
63
+ //# sourceMappingURL=gc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../src/geometry/gc.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAOzB,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAuBrE;AAgBD,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,mHAAmH;IACnH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC,EACzB,OAAO,GAAE,YAAY,GAAG;IACtB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CACxD,GACL,OAAO,CAAC,aAAa,CAAC,CAsBxB;AAmBD,kEAAkE;AAClE,wBAAsB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ3F"}
@@ -0,0 +1,109 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { ENTITY_KEY, GEOMETRY_KEY, entitiesMap, geometryMap, } from '../doc/schema.js';
5
+ /**
6
+ * Collect every blob hash currently referenced from a Y.Doc.
7
+ *
8
+ * An entity's `geometryRef.geomId` points at a `geometry` top-level
9
+ * entry whose `blobHash` (if any) we want to keep. We also include any
10
+ * `blobHash` that appears directly in `geometry.params.<*>` so apps
11
+ * that store auxiliary blob refs in params (e.g. textures) survive
12
+ * gc.
13
+ */
14
+ export function collectReferencedBlobHashes(doc) {
15
+ const referenced = new Set();
16
+ const geom = geometryMap(doc);
17
+ const ents = entitiesMap(doc);
18
+ // 1. From every entity's geometryRef → resolve to a geometry node.
19
+ ents.forEach((entUntyped) => {
20
+ const entity = entUntyped;
21
+ const refMap = entity.get(ENTITY_KEY.GEOMETRY_REF);
22
+ const geomId = refMap?.get('geomId');
23
+ if (typeof geomId !== 'string')
24
+ return;
25
+ addBlobHashesFromGeometry(geom.get(geomId), referenced);
26
+ });
27
+ // 2. Also include unreferenced-by-entity geometry entries' blobs —
28
+ // they are reachable via `geometry` and may get a fresh entity ref
29
+ // in the next transaction. We *only* sweep blobs that aren't even
30
+ // in any geometry entry.
31
+ geom.forEach((nodeUntyped) => {
32
+ addBlobHashesFromGeometry(nodeUntyped, referenced);
33
+ });
34
+ return referenced;
35
+ }
36
+ function addBlobHashesFromGeometry(node, out) {
37
+ if (!node)
38
+ return;
39
+ const blobHash = node.get(GEOMETRY_KEY.BLOB_HASH);
40
+ if (typeof blobHash === 'string')
41
+ out.add(blobHash);
42
+ const params = node.get(GEOMETRY_KEY.PARAMS);
43
+ if (!params)
44
+ return;
45
+ params.forEach((value) => {
46
+ if (typeof value === 'string' && /^[a-f0-9]{32}$/.test(value))
47
+ out.add(value);
48
+ });
49
+ }
50
+ /**
51
+ * Plan a blob sweep against `store`, retaining everything in `referenced`.
52
+ *
53
+ * The store's `list()` returns hashes only; size info comes from a
54
+ * `metaProvider` (default: walks `store.get(hash).byteLength`).
55
+ */
56
+ export async function planBlobSweep(store, referenced, options = {}) {
57
+ const epochMs = options.epochMs ?? 60_000;
58
+ const now = options.now ? options.now() : Date.now();
59
+ const all = await store.list();
60
+ const drop = [];
61
+ let reclaim = 0;
62
+ for (const hash of all) {
63
+ if (referenced.has(hash))
64
+ continue;
65
+ const meta = options.metaProvider
66
+ ? await options.metaProvider(hash)
67
+ : await metaFromGet(store, hash);
68
+ if (!meta)
69
+ continue;
70
+ const ageMs = meta.uploadedAt != null
71
+ ? Math.max(0, now - new Date(meta.uploadedAt).getTime())
72
+ : Number.POSITIVE_INFINITY;
73
+ if (ageMs >= epochMs) {
74
+ drop.push(hash);
75
+ reclaim += meta.byteLength;
76
+ }
77
+ }
78
+ return { drop, reclaimBytes: reclaim };
79
+ }
80
+ async function metaFromGet(store, hash) {
81
+ // Prefer cheap stat() if the backend offers it (size + uploadedAt
82
+ // without paying for the bytes).
83
+ if (typeof store.stat === 'function') {
84
+ const meta = await store.stat(hash);
85
+ if (meta)
86
+ return meta;
87
+ }
88
+ const bytes = await store.get(hash);
89
+ if (!bytes)
90
+ return null;
91
+ return {
92
+ hash,
93
+ byteLength: bytes.byteLength,
94
+ // No uploadedAt available via raw bytes — without it, the blob is
95
+ // treated as unbounded-old (always eligible).
96
+ };
97
+ }
98
+ /** Apply a sweep decision: delete the candidates from `store`. */
99
+ export async function sweepBlobs(store, decision) {
100
+ let freed = 0;
101
+ for (const hash of decision.drop) {
102
+ const ok = await store.delete(hash);
103
+ if (ok)
104
+ freed += 1;
105
+ }
106
+ void freed;
107
+ return decision.reclaimBytes;
108
+ }
109
+ //# sourceMappingURL=gc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc.js","sourceRoot":"","sources":["../../src/geometry/gc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from './blob-store.js';
2
+ export * from './csg.js';
3
+ export * from './gc.js';
4
+ export * from './parametric.js';
5
+ export * from './determinism.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/geometry/index.ts"],"names":[],"mappings":"AAIA,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ export * from './blob-store.js';
5
+ export * from './csg.js';
6
+ export * from './gc.js';
7
+ export * from './parametric.js';
8
+ export * from './determinism.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/geometry/index.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Parametric mesh primitives (spec §11.2 — v0.3 reference impl).
3
+ *
4
+ * The Rust `ifc-lite-geometry` kernel does the production work.
5
+ * Until the kernel is wired into the collab pipeline, this module
6
+ * ships a pure-TypeScript reference implementation for the most
7
+ * common parametric primitives:
8
+ *
9
+ * - extruded-area-solid (rectangle + circle profiles)
10
+ * - box (length × width × height)
11
+ * - cylinder (radius, height)
12
+ * - revolved-area-solid (circle profile around an axis)
13
+ *
14
+ * Output is deterministic given identical params. Hashing the
15
+ * `Mesh` (positions + indices) gives the same bytes on every machine
16
+ * because we use only deterministic float arithmetic — no Math.random,
17
+ * no transcendentals beyond Math.cos/sin which are IEEE-754 specified
18
+ * but not strictly reproducible across architectures. For tests we
19
+ * accept the small chance of cross-platform drift; the
20
+ * `determinism` harness flags it explicitly so consumers know when to
21
+ * fall back to mesh-blob upload.
22
+ */
23
+ export type ParametricSource = 'extruded-area-solid' | 'box' | 'cylinder' | 'revolved-area-solid';
24
+ export interface RectProfile {
25
+ type: 'rectangle';
26
+ width: number;
27
+ height: number;
28
+ }
29
+ export interface CircleProfile {
30
+ type: 'circle';
31
+ radius: number;
32
+ /** Number of segments for circle tessellation. Default 16. */
33
+ segments?: number;
34
+ }
35
+ export type Profile = RectProfile | CircleProfile;
36
+ export interface ExtrudedAreaSolidParams {
37
+ profile: Profile;
38
+ /** Extrusion direction (unit-ish). Default [0, 0, 1]. */
39
+ direction?: [number, number, number];
40
+ /** Extrusion depth. */
41
+ depth: number;
42
+ }
43
+ export interface BoxParams {
44
+ length: number;
45
+ width: number;
46
+ height: number;
47
+ }
48
+ export interface CylinderParams {
49
+ radius: number;
50
+ height: number;
51
+ segments?: number;
52
+ }
53
+ export interface RevolvedAreaSolidParams {
54
+ profile: CircleProfile;
55
+ /** Axis to revolve around. Default [0, 0, 1] (Z axis). */
56
+ axis?: [number, number, number];
57
+ /** Total revolution in radians. Default 2π. */
58
+ angle?: number;
59
+ /** Number of segments around the axis. Default 16. */
60
+ segments?: number;
61
+ }
62
+ export interface Mesh {
63
+ positions: Float32Array;
64
+ indices: Uint32Array;
65
+ }
66
+ /**
67
+ * Build a mesh from `(source, params)`. Throws if the source is not
68
+ * recognized — the conflict detector / kernel hookup should fall back
69
+ * to mesh-blob upload in that case.
70
+ */
71
+ export declare function paramsToMesh(source: ParametricSource, params: unknown): Mesh;
72
+ export declare function extrudedAreaSolid(params: ExtrudedAreaSolidParams): Mesh;
73
+ export declare function box(params: BoxParams): Mesh;
74
+ export declare function cylinder(params: CylinderParams): Mesh;
75
+ export declare function revolvedAreaSolid(params: RevolvedAreaSolidParams): Mesh;
76
+ /**
77
+ * Hash a mesh for determinism / cache-key purposes.
78
+ *
79
+ * 128-bit digest built by running FNV-1a four times in parallel with
80
+ * different IVs over a stable byte ordering of positions+indices.
81
+ * Returned as 32 hex chars. Position floats are quantized to 8 decimal
82
+ * places before hashing so trivial floating-point drift doesn't break
83
+ * cache lookups.
84
+ *
85
+ * Note: this is not a cryptographic hash — collision-resistance is
86
+ * "good enough for cache keys and drift detection," not "good enough
87
+ * for security." The previous implementation returned `.repeat(4)` of
88
+ * a single 32-bit digest, which looked 128-bit but had only 32 bits of
89
+ * entropy; the four-IV variant restores real width.
90
+ */
91
+ export declare function hashMesh(mesh: Mesh): string;
92
+ //# sourceMappingURL=parametric.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parametric.d.ts","sourceRoot":"","sources":["../../src/geometry/parametric.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,gBAAgB,GACxB,qBAAqB,GACrB,KAAK,GACL,UAAU,GACV,qBAAqB,CAAC;AAE1B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,MAAM,OAAO,GAAG,WAAW,GAAG,aAAa,CAAC;AAElD,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACnB,SAAS,EAAE,YAAY,CAAC;IACxB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAa5E;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAuCvE;AA0BD,wBAAgB,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAM3C;AAMD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAMrD;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAwDvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CA4B3C"}