@panproto/core 0.2.0 → 0.4.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/dist/index.cjs CHANGED
@@ -52,7 +52,48 @@ async function loadWasm(input) {
52
52
  put_record: glue.put_record,
53
53
  compose_migrations: glue.compose_migrations,
54
54
  diff_schemas: glue.diff_schemas,
55
- free_handle: glue.free_handle
55
+ free_handle: glue.free_handle,
56
+ diff_schemas_full: glue.diff_schemas_full,
57
+ classify_diff: glue.classify_diff,
58
+ report_text: glue.report_text,
59
+ report_json: glue.report_json,
60
+ normalize_schema: glue.normalize_schema,
61
+ validate_schema: glue.validate_schema,
62
+ register_io_protocols: glue.register_io_protocols,
63
+ list_io_protocols: glue.list_io_protocols,
64
+ parse_instance: glue.parse_instance,
65
+ emit_instance: glue.emit_instance,
66
+ validate_instance: glue.validate_instance,
67
+ instance_to_json: glue.instance_to_json,
68
+ json_to_instance: glue.json_to_instance,
69
+ instance_element_count: glue.instance_element_count,
70
+ lens_from_combinators: glue.lens_from_combinators,
71
+ check_lens_laws: glue.check_lens_laws,
72
+ check_get_put: glue.check_get_put,
73
+ check_put_get: glue.check_put_get,
74
+ invert_migration: glue.invert_migration,
75
+ compose_lenses: glue.compose_lenses,
76
+ // Phase 4
77
+ list_builtin_protocols: glue.list_builtin_protocols,
78
+ get_builtin_protocol: glue.get_builtin_protocol,
79
+ // Phase 5
80
+ create_theory: glue.create_theory,
81
+ colimit_theories: glue.colimit_theories,
82
+ check_morphism: glue.check_morphism,
83
+ migrate_model: glue.migrate_model,
84
+ // Phase 6
85
+ vcs_init: glue.vcs_init,
86
+ vcs_add: glue.vcs_add,
87
+ vcs_commit: glue.vcs_commit,
88
+ vcs_log: glue.vcs_log,
89
+ vcs_status: glue.vcs_status,
90
+ vcs_diff: glue.vcs_diff,
91
+ vcs_branch: glue.vcs_branch,
92
+ vcs_checkout: glue.vcs_checkout,
93
+ vcs_merge: glue.vcs_merge,
94
+ vcs_stash: glue.vcs_stash,
95
+ vcs_stash_pop: glue.vcs_stash_pop,
96
+ vcs_blame: glue.vcs_blame
56
97
  };
57
98
  const memory = initOutput.memory;
58
99
  if (!memory) {
@@ -119,6 +160,280 @@ function packSchemaOps(ops) {
119
160
  function packMigrationMapping(mapping) {
120
161
  return msgpack.encode(mapping);
121
162
  }
163
+ function renameField(oldName, newName) {
164
+ return { type: "rename-field", old: oldName, new: newName };
165
+ }
166
+ function addField(name, vertexKind, defaultValue) {
167
+ return { type: "add-field", name, vertexKind, default: defaultValue };
168
+ }
169
+ function removeField(name) {
170
+ return { type: "remove-field", name };
171
+ }
172
+ function wrapInObject(fieldName) {
173
+ return { type: "wrap-in-object", fieldName };
174
+ }
175
+ function hoistField(host, field) {
176
+ return { type: "hoist-field", host, field };
177
+ }
178
+ function coerceType(fromKind, toKind) {
179
+ return { type: "coerce-type", fromKind, toKind };
180
+ }
181
+ function compose(first, second) {
182
+ return { type: "compose", first, second };
183
+ }
184
+ function pipeline(combinators) {
185
+ const [first, ...rest] = combinators;
186
+ return rest.reduce((acc, c) => compose(acc, c), first);
187
+ }
188
+ class LensHandle {
189
+ #handle;
190
+ #wasm;
191
+ constructor(handle, wasm) {
192
+ this.#handle = handle;
193
+ this.#wasm = wasm;
194
+ }
195
+ /** The underlying WASM handle. Internal use only. */
196
+ get _handle() {
197
+ return this.#handle;
198
+ }
199
+ /**
200
+ * Forward projection: extract the view from a record.
201
+ *
202
+ * @param record - MessagePack-encoded input record
203
+ * @returns The projected view and opaque complement bytes
204
+ * @throws {@link WasmError} if the WASM call fails
205
+ */
206
+ get(record) {
207
+ try {
208
+ const outputBytes = this.#wasm.exports.get_record(
209
+ this.#handle.id,
210
+ record
211
+ );
212
+ const result = unpackFromWasm(outputBytes);
213
+ return {
214
+ view: result.view,
215
+ complement: result.complement instanceof Uint8Array ? result.complement : new Uint8Array(result.complement)
216
+ };
217
+ } catch (error) {
218
+ throw new WasmError(
219
+ `get_record failed: ${error instanceof Error ? error.message : String(error)}`,
220
+ { cause: error }
221
+ );
222
+ }
223
+ }
224
+ /**
225
+ * Backward put: restore a full record from a modified view and complement.
226
+ *
227
+ * @param view - MessagePack-encoded (possibly modified) projected view
228
+ * @param complement - The complement from a prior `get()` call
229
+ * @returns The restored full record
230
+ * @throws {@link WasmError} if the WASM call fails
231
+ */
232
+ put(view, complement) {
233
+ try {
234
+ const outputBytes = this.#wasm.exports.put_record(
235
+ this.#handle.id,
236
+ view,
237
+ complement
238
+ );
239
+ const data = unpackFromWasm(outputBytes);
240
+ return { data };
241
+ } catch (error) {
242
+ throw new WasmError(
243
+ `put_record failed: ${error instanceof Error ? error.message : String(error)}`,
244
+ { cause: error }
245
+ );
246
+ }
247
+ }
248
+ /**
249
+ * Check both GetPut and PutGet lens laws for an instance.
250
+ *
251
+ * @param instance - MessagePack-encoded instance data
252
+ * @returns Whether both laws hold and any violation message
253
+ * @throws {@link WasmError} if the WASM call fails
254
+ */
255
+ checkLaws(instance) {
256
+ try {
257
+ const resultBytes = this.#wasm.exports.check_lens_laws(
258
+ this.#handle.id,
259
+ instance
260
+ );
261
+ return unpackFromWasm(resultBytes);
262
+ } catch (error) {
263
+ throw new WasmError(
264
+ `check_lens_laws failed: ${error instanceof Error ? error.message : String(error)}`,
265
+ { cause: error }
266
+ );
267
+ }
268
+ }
269
+ /**
270
+ * Check the GetPut lens law for an instance.
271
+ *
272
+ * @param instance - MessagePack-encoded instance data
273
+ * @returns Whether the law holds and any violation message
274
+ * @throws {@link WasmError} if the WASM call fails
275
+ */
276
+ checkGetPut(instance) {
277
+ try {
278
+ const resultBytes = this.#wasm.exports.check_get_put(
279
+ this.#handle.id,
280
+ instance
281
+ );
282
+ return unpackFromWasm(resultBytes);
283
+ } catch (error) {
284
+ throw new WasmError(
285
+ `check_get_put failed: ${error instanceof Error ? error.message : String(error)}`,
286
+ { cause: error }
287
+ );
288
+ }
289
+ }
290
+ /**
291
+ * Check the PutGet lens law for an instance.
292
+ *
293
+ * @param instance - MessagePack-encoded instance data
294
+ * @returns Whether the law holds and any violation message
295
+ * @throws {@link WasmError} if the WASM call fails
296
+ */
297
+ checkPutGet(instance) {
298
+ try {
299
+ const resultBytes = this.#wasm.exports.check_put_get(
300
+ this.#handle.id,
301
+ instance
302
+ );
303
+ return unpackFromWasm(resultBytes);
304
+ } catch (error) {
305
+ throw new WasmError(
306
+ `check_put_get failed: ${error instanceof Error ? error.message : String(error)}`,
307
+ { cause: error }
308
+ );
309
+ }
310
+ }
311
+ /** Release the underlying WASM resource. */
312
+ [Symbol.dispose]() {
313
+ this.#handle[Symbol.dispose]();
314
+ }
315
+ }
316
+ function fromCombinators(schema, protocol, wasm, ...combinators) {
317
+ const wireCombs = combinators.map(combinatorToWire);
318
+ const combBytes = packToWasm(wireCombs);
319
+ try {
320
+ const rawHandle = wasm.exports.lens_from_combinators(
321
+ schema._handle.id,
322
+ protocol._handle.id,
323
+ combBytes
324
+ );
325
+ const handle = createHandle(rawHandle, wasm);
326
+ return new LensHandle(handle, wasm);
327
+ } catch (error) {
328
+ throw new WasmError(
329
+ `lens_from_combinators failed: ${error instanceof Error ? error.message : String(error)}`,
330
+ { cause: error }
331
+ );
332
+ }
333
+ }
334
+ function combinatorToWire(combinator) {
335
+ switch (combinator.type) {
336
+ case "rename-field":
337
+ return { RenameField: { old: combinator.old, new: combinator.new } };
338
+ case "add-field":
339
+ return { AddField: { name: combinator.name, vertex_kind: combinator.vertexKind, default: combinator.default } };
340
+ case "remove-field":
341
+ return { RemoveField: { name: combinator.name } };
342
+ case "wrap-in-object":
343
+ return { WrapInObject: { field_name: combinator.fieldName } };
344
+ case "hoist-field":
345
+ return { HoistField: { host: combinator.host, field: combinator.field } };
346
+ case "coerce-type":
347
+ return { CoerceType: { from_kind: combinator.fromKind, to_kind: combinator.toKind } };
348
+ case "compose":
349
+ return { Compose: [combinatorToWire(combinator.first), combinatorToWire(combinator.second)] };
350
+ }
351
+ }
352
+ class FullDiffReport {
353
+ /** The raw diff data. */
354
+ data;
355
+ /** @internal */
356
+ _reportBytes;
357
+ /** @internal */
358
+ _wasm;
359
+ /** @internal */
360
+ constructor(data, reportBytes, wasm) {
361
+ this.data = data;
362
+ this._reportBytes = reportBytes;
363
+ this._wasm = wasm;
364
+ }
365
+ /** Whether there are any changes at all. */
366
+ get hasChanges() {
367
+ const d = this.data;
368
+ return d.added_vertices.length > 0 || d.removed_vertices.length > 0 || d.kind_changes.length > 0 || d.added_edges.length > 0 || d.removed_edges.length > 0 || Object.keys(d.modified_constraints).length > 0 || d.added_hyper_edges.length > 0 || d.removed_hyper_edges.length > 0 || d.modified_hyper_edges.length > 0 || Object.keys(d.added_required).length > 0 || Object.keys(d.removed_required).length > 0 || Object.keys(d.added_nsids).length > 0 || d.removed_nsids.length > 0 || d.changed_nsids.length > 0 || d.added_variants.length > 0 || d.removed_variants.length > 0 || d.modified_variants.length > 0 || d.order_changes.length > 0 || d.added_recursion_points.length > 0 || d.removed_recursion_points.length > 0 || d.modified_recursion_points.length > 0 || d.usage_mode_changes.length > 0 || d.added_spans.length > 0 || d.removed_spans.length > 0 || d.modified_spans.length > 0 || d.nominal_changes.length > 0;
369
+ }
370
+ /** Classify the diff against a protocol, producing a compatibility report. */
371
+ classify(protocol) {
372
+ const rawBytes = this._wasm.exports.classify_diff(
373
+ protocol._handle.id,
374
+ this._reportBytes
375
+ );
376
+ const data = unpackFromWasm(rawBytes);
377
+ return new CompatReport(data, rawBytes, this._wasm);
378
+ }
379
+ }
380
+ class CompatReport {
381
+ /** The raw report data. */
382
+ data;
383
+ /** @internal */
384
+ _rawBytes;
385
+ /** @internal */
386
+ _wasm;
387
+ /** @internal */
388
+ constructor(data, rawBytes, wasm) {
389
+ this.data = data;
390
+ this._rawBytes = rawBytes;
391
+ this._wasm = wasm;
392
+ }
393
+ /** List of breaking changes. */
394
+ get breakingChanges() {
395
+ return this.data.breaking;
396
+ }
397
+ /** List of non-breaking changes. */
398
+ get nonBreakingChanges() {
399
+ return this.data.non_breaking;
400
+ }
401
+ /** Whether the changes are fully compatible (no breaking changes). */
402
+ get isCompatible() {
403
+ return this.data.compatible;
404
+ }
405
+ /** Whether there are any breaking changes. */
406
+ get isBreaking() {
407
+ return !this.data.compatible;
408
+ }
409
+ /** Whether the changes are backward-compatible (additions only, no removals). */
410
+ get isBackwardCompatible() {
411
+ return this.data.compatible;
412
+ }
413
+ /** Render as human-readable text. */
414
+ toText() {
415
+ return this._wasm.exports.report_text(this._rawBytes);
416
+ }
417
+ /** Render as a JSON string. */
418
+ toJson() {
419
+ return this._wasm.exports.report_json(this._rawBytes);
420
+ }
421
+ }
422
+ class ValidationResult {
423
+ /** The list of validation issues found. */
424
+ issues;
425
+ constructor(issues) {
426
+ this.issues = issues;
427
+ }
428
+ /** Whether the schema is valid (no issues found). */
429
+ get isValid() {
430
+ return this.issues.length === 0;
431
+ }
432
+ /** The number of validation issues. */
433
+ get errorCount() {
434
+ return this.issues.length;
435
+ }
436
+ }
122
437
  class SchemaBuilder {
123
438
  #protocolName;
124
439
  #protocolHandle;
@@ -353,7 +668,13 @@ class SchemaBuilder {
353
668
  ),
354
669
  required: Object.fromEntries(
355
670
  Array.from(this.#required.entries()).map(([k, v]) => [k, [...v]])
356
- )
671
+ ),
672
+ variants: {},
673
+ orderings: {},
674
+ recursionPoints: {},
675
+ usageModes: {},
676
+ spans: {},
677
+ nominal: {}
357
678
  };
358
679
  return new BuiltSchema(handle, data, this.#wasm);
359
680
  }
@@ -391,6 +712,25 @@ class BuiltSchema {
391
712
  get edges() {
392
713
  return this.#data.edges;
393
714
  }
715
+ /** @internal Create from raw handle (used by normalize). */
716
+ static _fromHandle(handle, data, _protocol, wasm) {
717
+ const wasmHandle = createHandle(handle, wasm);
718
+ return new BuiltSchema(wasmHandle, data, wasm);
719
+ }
720
+ /** Normalize this schema by collapsing reference chains. Returns a new BuiltSchema. */
721
+ normalize() {
722
+ const handle = this.#wasm.exports.normalize_schema(this.#handle.id);
723
+ return BuiltSchema._fromHandle(handle, this.#data, this.#data.protocol, this.#wasm);
724
+ }
725
+ /** Validate this schema against a protocol's rules. */
726
+ validate(protocol) {
727
+ const bytes = this.#wasm.exports.validate_schema(
728
+ this.#handle.id,
729
+ protocol._handle.id
730
+ );
731
+ const issues = unpackFromWasm(bytes);
732
+ return new ValidationResult(issues);
733
+ }
394
734
  /** Release the WASM-side schema resource. */
395
735
  [Symbol.dispose]() {
396
736
  this.#handle[Symbol.dispose]();
@@ -413,6 +753,18 @@ class Protocol {
413
753
  get spec() {
414
754
  return this.#spec;
415
755
  }
756
+ /** The edge rules for this protocol. */
757
+ get edgeRules() {
758
+ return this.#spec.edgeRules;
759
+ }
760
+ /** The constraint sorts for this protocol. */
761
+ get constraintSorts() {
762
+ return this.#spec.constraintSorts;
763
+ }
764
+ /** The object kinds for this protocol. */
765
+ get objectKinds() {
766
+ return this.#spec.objKinds;
767
+ }
416
768
  /** The WASM handle. Internal use only. */
417
769
  get _handle() {
418
770
  return this.#handle;
@@ -553,6 +905,34 @@ const BUILTIN_PROTOCOLS = /* @__PURE__ */ new Map([
553
905
  ["graphql", GRAPHQL_SPEC],
554
906
  ["json-schema", JSON_SCHEMA_SPEC]
555
907
  ]);
908
+ let _protocolNamesCache = null;
909
+ function getProtocolNames(wasm) {
910
+ if (_protocolNamesCache !== null) return _protocolNamesCache;
911
+ const bytes = wasm.exports.list_builtin_protocols();
912
+ _protocolNamesCache = unpackFromWasm(bytes);
913
+ return _protocolNamesCache;
914
+ }
915
+ function getBuiltinProtocol(name, wasm) {
916
+ try {
917
+ const nameBytes = new TextEncoder().encode(name);
918
+ const bytes = wasm.exports.get_builtin_protocol(nameBytes);
919
+ const wire = unpackFromWasm(bytes);
920
+ return {
921
+ name: wire.name,
922
+ schemaTheory: wire.schema_theory,
923
+ instanceTheory: wire.instance_theory,
924
+ edgeRules: wire.edge_rules.map((r) => ({
925
+ edgeKind: r.edge_kind,
926
+ srcKinds: r.src_kinds,
927
+ tgtKinds: r.tgt_kinds
928
+ })),
929
+ objKinds: wire.obj_kinds,
930
+ constraintSorts: wire.constraint_sorts
931
+ };
932
+ } catch {
933
+ return void 0;
934
+ }
935
+ }
556
936
  class MigrationBuilder {
557
937
  #src;
558
938
  #tgt;
@@ -637,6 +1017,50 @@ class MigrationBuilder {
637
1017
  resolvers: [...this.#resolvers]
638
1018
  };
639
1019
  }
1020
+ /**
1021
+ * Invert a bijective migration.
1022
+ *
1023
+ * Serializes the current mapping to MessagePack and calls the
1024
+ * `invert_migration` WASM entry point. Returns a new MigrationSpec
1025
+ * representing the inverted migration.
1026
+ *
1027
+ * @returns The inverted migration specification
1028
+ * @throws {@link MigrationError} if the migration is not bijective or inversion fails
1029
+ */
1030
+ invert() {
1031
+ const edgeMap = new Map(
1032
+ this.#edgeMap.map(([src, tgt]) => [
1033
+ { src: src.src, tgt: src.tgt, kind: src.kind, name: src.name ?? null },
1034
+ { src: tgt.src, tgt: tgt.tgt, kind: tgt.kind, name: tgt.name ?? null }
1035
+ ])
1036
+ );
1037
+ const resolver = new Map(
1038
+ this.#resolvers.map(([[s, t], e]) => [
1039
+ [s, t],
1040
+ { src: e.src, tgt: e.tgt, kind: e.kind, name: e.name ?? null }
1041
+ ])
1042
+ );
1043
+ const mapping = packMigrationMapping({
1044
+ vertex_map: Object.fromEntries(this.#vertexMap),
1045
+ edge_map: edgeMap,
1046
+ hyper_edge_map: {},
1047
+ label_map: /* @__PURE__ */ new Map(),
1048
+ resolver
1049
+ });
1050
+ try {
1051
+ const resultBytes = this.#wasm.exports.invert_migration(
1052
+ mapping,
1053
+ this.#src._handle.id,
1054
+ this.#tgt._handle.id
1055
+ );
1056
+ return unpackFromWasm(resultBytes);
1057
+ } catch (error) {
1058
+ throw new MigrationError(
1059
+ `Failed to invert migration: ${error instanceof Error ? error.message : String(error)}`,
1060
+ { cause: error }
1061
+ );
1062
+ }
1063
+ }
640
1064
  /**
641
1065
  * Compile the migration for fast per-record application.
642
1066
  *
@@ -837,6 +1261,433 @@ function composeMigrations(m1, m2, wasm) {
837
1261
  );
838
1262
  }
839
1263
  }
1264
+ class Instance {
1265
+ /** Raw MsgPack-encoded instance bytes (for passing back to WASM). */
1266
+ _bytes;
1267
+ /** The schema this instance conforms to. */
1268
+ _schema;
1269
+ /** @internal */
1270
+ _wasm;
1271
+ constructor(bytes, schema, wasm) {
1272
+ this._bytes = bytes;
1273
+ this._schema = schema;
1274
+ this._wasm = wasm;
1275
+ }
1276
+ /** Convert this instance to JSON bytes. */
1277
+ toJson() {
1278
+ return this._wasm.exports.instance_to_json(
1279
+ this._schema._handle.id,
1280
+ this._bytes
1281
+ );
1282
+ }
1283
+ /** Validate this instance against its schema. */
1284
+ validate() {
1285
+ const resultBytes = this._wasm.exports.validate_instance(
1286
+ this._schema._handle.id,
1287
+ this._bytes
1288
+ );
1289
+ const errors = unpackFromWasm(resultBytes);
1290
+ return {
1291
+ isValid: errors.length === 0,
1292
+ errors
1293
+ };
1294
+ }
1295
+ /** Get the number of elements in this instance. */
1296
+ get elementCount() {
1297
+ return this._wasm.exports.instance_element_count(this._bytes);
1298
+ }
1299
+ /**
1300
+ * Create an Instance from JSON input.
1301
+ *
1302
+ * @param schema - The schema the JSON data conforms to
1303
+ * @param json - JSON bytes
1304
+ * @param wasm - The WASM module
1305
+ * @returns A new Instance
1306
+ */
1307
+ static fromJson(schema, json, wasm) {
1308
+ const bytes = wasm.exports.json_to_instance(schema._handle.id, json);
1309
+ return new Instance(bytes, schema, wasm);
1310
+ }
1311
+ }
1312
+ const PROTOCOL_CATEGORIES = {
1313
+ annotation: [
1314
+ "brat",
1315
+ "conllu",
1316
+ "naf",
1317
+ "uima",
1318
+ "folia",
1319
+ "tei",
1320
+ "timeml",
1321
+ "elan",
1322
+ "iso_space",
1323
+ "paula",
1324
+ "laf_graf",
1325
+ "decomp",
1326
+ "ucca",
1327
+ "fovea",
1328
+ "bead",
1329
+ "web_annotation",
1330
+ "amr",
1331
+ "concrete",
1332
+ "nif"
1333
+ ],
1334
+ api: ["graphql", "openapi", "asyncapi", "jsonapi", "raml"],
1335
+ config: ["cloudformation", "ansible", "k8s_crd", "hcl"],
1336
+ data_schema: [
1337
+ "json_schema",
1338
+ "yaml_schema",
1339
+ "toml_schema",
1340
+ "cddl",
1341
+ "bson",
1342
+ "csv_table",
1343
+ "ini_schema"
1344
+ ],
1345
+ data_science: ["dataframe", "parquet", "arrow"],
1346
+ database: ["mongodb", "dynamodb", "cassandra", "neo4j", "sql", "redis"],
1347
+ domain: ["geojson", "fhir", "rss_atom", "vcard_ical", "swift_mt", "edi_x12"],
1348
+ serialization: [
1349
+ "protobuf",
1350
+ "avro",
1351
+ "thrift",
1352
+ "capnproto",
1353
+ "flatbuffers",
1354
+ "asn1",
1355
+ "bond",
1356
+ "msgpack_schema"
1357
+ ],
1358
+ type_system: [
1359
+ "typescript",
1360
+ "python",
1361
+ "rust_serde",
1362
+ "java",
1363
+ "go_struct",
1364
+ "kotlin",
1365
+ "csharp",
1366
+ "swift"
1367
+ ],
1368
+ web_document: [
1369
+ "atproto",
1370
+ "jsx",
1371
+ "vue",
1372
+ "svelte",
1373
+ "css",
1374
+ "html",
1375
+ "markdown",
1376
+ "xml_xsd",
1377
+ "docx",
1378
+ "odf"
1379
+ ]
1380
+ };
1381
+ const encoder$1 = new TextEncoder();
1382
+ class IoRegistry {
1383
+ _handle;
1384
+ _wasm;
1385
+ _protocolsCache = null;
1386
+ constructor(handle, wasm) {
1387
+ this._handle = handle;
1388
+ this._wasm = wasm;
1389
+ }
1390
+ /**
1391
+ * List all registered protocol names.
1392
+ *
1393
+ * The result is cached after the first call.
1394
+ */
1395
+ get protocols() {
1396
+ if (this._protocolsCache === null) {
1397
+ const bytes = this._wasm.exports.list_io_protocols(this._handle.id);
1398
+ this._protocolsCache = unpackFromWasm(bytes);
1399
+ }
1400
+ return this._protocolsCache;
1401
+ }
1402
+ /** Protocol names organized by category. */
1403
+ get categories() {
1404
+ return PROTOCOL_CATEGORIES;
1405
+ }
1406
+ /** Check if a protocol is registered. */
1407
+ hasProtocol(name) {
1408
+ return this.protocols.includes(name);
1409
+ }
1410
+ /**
1411
+ * Parse raw format bytes into an Instance.
1412
+ *
1413
+ * @param protocolName - The protocol codec name (e.g., 'graphql', 'protobuf')
1414
+ * @param schema - The schema the data conforms to
1415
+ * @param input - Raw format bytes to parse
1416
+ * @returns A new Instance wrapping the parsed data
1417
+ * @throws {@link PanprotoError} if the protocol is not registered or parsing fails
1418
+ */
1419
+ parse(protocolName, schema, input) {
1420
+ const nameBytes = encoder$1.encode(protocolName);
1421
+ const resultBytes = this._wasm.exports.parse_instance(
1422
+ this._handle.id,
1423
+ nameBytes,
1424
+ schema._handle.id,
1425
+ input
1426
+ );
1427
+ return new Instance(resultBytes, schema, this._wasm);
1428
+ }
1429
+ /**
1430
+ * Emit an Instance to raw format bytes.
1431
+ *
1432
+ * @param protocolName - The protocol codec name (e.g., 'graphql', 'protobuf')
1433
+ * @param schema - The schema the instance conforms to
1434
+ * @param instance - The instance to emit
1435
+ * @returns Raw format bytes
1436
+ * @throws {@link PanprotoError} if the protocol is not registered or emission fails
1437
+ */
1438
+ emit(protocolName, schema, instance) {
1439
+ const nameBytes = encoder$1.encode(protocolName);
1440
+ return this._wasm.exports.emit_instance(
1441
+ this._handle.id,
1442
+ nameBytes,
1443
+ schema._handle.id,
1444
+ instance._bytes
1445
+ );
1446
+ }
1447
+ /** Release the WASM-side IoRegistry resource. */
1448
+ [Symbol.dispose]() {
1449
+ this._handle[Symbol.dispose]();
1450
+ }
1451
+ }
1452
+ const encoder = new TextEncoder();
1453
+ class Repository {
1454
+ #handle;
1455
+ #wasm;
1456
+ #protocolName;
1457
+ constructor(handle, protocolName, wasm) {
1458
+ this.#handle = handle;
1459
+ this.#protocolName = protocolName;
1460
+ this.#wasm = wasm;
1461
+ }
1462
+ /**
1463
+ * Initialize a new in-memory repository.
1464
+ *
1465
+ * @param protocolName - The protocol this repository tracks
1466
+ * @param wasm - The WASM module
1467
+ * @returns A new disposable Repository
1468
+ */
1469
+ static init(protocolName, wasm) {
1470
+ const nameBytes = encoder.encode(protocolName);
1471
+ const rawHandle = wasm.exports.vcs_init(nameBytes);
1472
+ const handle = createHandle(rawHandle, wasm);
1473
+ return new Repository(handle, protocolName, wasm);
1474
+ }
1475
+ /** The protocol name this repository tracks. */
1476
+ get protocolName() {
1477
+ return this.#protocolName;
1478
+ }
1479
+ /** The underlying WASM handle. Internal use only. */
1480
+ get _handle() {
1481
+ return this.#handle;
1482
+ }
1483
+ /**
1484
+ * Stage a schema for the next commit.
1485
+ *
1486
+ * @param schema - The built schema to stage
1487
+ * @returns An object with the schema's object ID
1488
+ */
1489
+ add(schema) {
1490
+ try {
1491
+ const resultBytes = this.#wasm.exports.vcs_add(
1492
+ this.#handle.id,
1493
+ schema._handle.id
1494
+ );
1495
+ const result = unpackFromWasm(resultBytes);
1496
+ return { schemaId: result.schema_id };
1497
+ } catch (error) {
1498
+ throw new WasmError(
1499
+ `vcs_add failed: ${error instanceof Error ? error.message : String(error)}`,
1500
+ { cause: error }
1501
+ );
1502
+ }
1503
+ }
1504
+ /**
1505
+ * Create a commit from the current staging area.
1506
+ *
1507
+ * @param message - The commit message
1508
+ * @param author - The commit author
1509
+ * @returns The commit result
1510
+ */
1511
+ commit(message, author) {
1512
+ try {
1513
+ return this.#wasm.exports.vcs_commit(
1514
+ this.#handle.id,
1515
+ encoder.encode(message),
1516
+ encoder.encode(author)
1517
+ );
1518
+ } catch (error) {
1519
+ throw new WasmError(
1520
+ `vcs_commit failed: ${error instanceof Error ? error.message : String(error)}`,
1521
+ { cause: error }
1522
+ );
1523
+ }
1524
+ }
1525
+ /**
1526
+ * Walk the commit log from HEAD.
1527
+ *
1528
+ * @param count - Maximum number of log entries to return (default: 50)
1529
+ * @returns Array of commit log entries
1530
+ */
1531
+ log(count = 50) {
1532
+ try {
1533
+ const resultBytes = this.#wasm.exports.vcs_log(this.#handle.id, count);
1534
+ return unpackFromWasm(resultBytes);
1535
+ } catch (error) {
1536
+ throw new WasmError(
1537
+ `vcs_log failed: ${error instanceof Error ? error.message : String(error)}`,
1538
+ { cause: error }
1539
+ );
1540
+ }
1541
+ }
1542
+ /**
1543
+ * Get the repository status.
1544
+ *
1545
+ * @returns Current branch and HEAD commit info
1546
+ */
1547
+ status() {
1548
+ try {
1549
+ const resultBytes = this.#wasm.exports.vcs_status(this.#handle.id);
1550
+ return unpackFromWasm(resultBytes);
1551
+ } catch (error) {
1552
+ throw new WasmError(
1553
+ `vcs_status failed: ${error instanceof Error ? error.message : String(error)}`,
1554
+ { cause: error }
1555
+ );
1556
+ }
1557
+ }
1558
+ /**
1559
+ * Get diff information for the repository.
1560
+ *
1561
+ * @returns Diff result with branch info
1562
+ */
1563
+ diff() {
1564
+ try {
1565
+ const resultBytes = this.#wasm.exports.vcs_diff(this.#handle.id);
1566
+ return unpackFromWasm(resultBytes);
1567
+ } catch (error) {
1568
+ throw new WasmError(
1569
+ `vcs_diff failed: ${error instanceof Error ? error.message : String(error)}`,
1570
+ { cause: error }
1571
+ );
1572
+ }
1573
+ }
1574
+ /**
1575
+ * Create a new branch at the current HEAD.
1576
+ *
1577
+ * @param name - The branch name
1578
+ * @returns Operation result
1579
+ */
1580
+ branch(name) {
1581
+ try {
1582
+ const resultBytes = this.#wasm.exports.vcs_branch(
1583
+ this.#handle.id,
1584
+ encoder.encode(name)
1585
+ );
1586
+ return unpackFromWasm(resultBytes);
1587
+ } catch (error) {
1588
+ throw new WasmError(
1589
+ `vcs_branch failed: ${error instanceof Error ? error.message : String(error)}`,
1590
+ { cause: error }
1591
+ );
1592
+ }
1593
+ }
1594
+ /**
1595
+ * Checkout a branch.
1596
+ *
1597
+ * @param target - The branch name to checkout
1598
+ * @returns Operation result
1599
+ */
1600
+ checkout(target) {
1601
+ try {
1602
+ const resultBytes = this.#wasm.exports.vcs_checkout(
1603
+ this.#handle.id,
1604
+ encoder.encode(target)
1605
+ );
1606
+ return unpackFromWasm(resultBytes);
1607
+ } catch (error) {
1608
+ throw new WasmError(
1609
+ `vcs_checkout failed: ${error instanceof Error ? error.message : String(error)}`,
1610
+ { cause: error }
1611
+ );
1612
+ }
1613
+ }
1614
+ /**
1615
+ * Merge a branch into the current branch.
1616
+ *
1617
+ * @param branchName - The branch to merge
1618
+ * @returns Operation result
1619
+ */
1620
+ merge(branchName) {
1621
+ try {
1622
+ const resultBytes = this.#wasm.exports.vcs_merge(
1623
+ this.#handle.id,
1624
+ encoder.encode(branchName)
1625
+ );
1626
+ return unpackFromWasm(resultBytes);
1627
+ } catch (error) {
1628
+ throw new WasmError(
1629
+ `vcs_merge failed: ${error instanceof Error ? error.message : String(error)}`,
1630
+ { cause: error }
1631
+ );
1632
+ }
1633
+ }
1634
+ /**
1635
+ * Stash the current working state.
1636
+ *
1637
+ * @returns Operation result
1638
+ */
1639
+ stash() {
1640
+ try {
1641
+ const resultBytes = this.#wasm.exports.vcs_stash(this.#handle.id);
1642
+ return unpackFromWasm(resultBytes);
1643
+ } catch (error) {
1644
+ throw new WasmError(
1645
+ `vcs_stash failed: ${error instanceof Error ? error.message : String(error)}`,
1646
+ { cause: error }
1647
+ );
1648
+ }
1649
+ }
1650
+ /**
1651
+ * Pop the most recent stash entry.
1652
+ *
1653
+ * @returns Operation result
1654
+ */
1655
+ stashPop() {
1656
+ try {
1657
+ const resultBytes = this.#wasm.exports.vcs_stash_pop(this.#handle.id);
1658
+ return unpackFromWasm(resultBytes);
1659
+ } catch (error) {
1660
+ throw new WasmError(
1661
+ `vcs_stash_pop failed: ${error instanceof Error ? error.message : String(error)}`,
1662
+ { cause: error }
1663
+ );
1664
+ }
1665
+ }
1666
+ /**
1667
+ * Find which commit introduced a vertex.
1668
+ *
1669
+ * @param vertexId - The vertex ID to blame
1670
+ * @returns Blame result with commit info
1671
+ */
1672
+ blame(vertexId) {
1673
+ try {
1674
+ const resultBytes = this.#wasm.exports.vcs_blame(
1675
+ this.#handle.id,
1676
+ encoder.encode(vertexId)
1677
+ );
1678
+ return unpackFromWasm(resultBytes);
1679
+ } catch (error) {
1680
+ throw new WasmError(
1681
+ `vcs_blame failed: ${error instanceof Error ? error.message : String(error)}`,
1682
+ { cause: error }
1683
+ );
1684
+ }
1685
+ }
1686
+ /** Release the WASM-side repository resource. */
1687
+ [Symbol.dispose]() {
1688
+ this.#handle[Symbol.dispose]();
1689
+ }
1690
+ }
840
1691
  class Panproto {
841
1692
  #wasm;
842
1693
  #protocols;
@@ -877,6 +1728,12 @@ class Panproto {
877
1728
  this.#protocols.set(name, proto);
878
1729
  return proto;
879
1730
  }
1731
+ const wasmSpec = getBuiltinProtocol(name, this.#wasm);
1732
+ if (wasmSpec) {
1733
+ const proto = defineProtocol(wasmSpec, this.#wasm);
1734
+ this.#protocols.set(name, proto);
1735
+ return proto;
1736
+ }
880
1737
  throw new PanprotoError(
881
1738
  `Protocol "${name}" not found. Register it with defineProtocol() first.`
882
1739
  );
@@ -937,6 +1794,31 @@ class Panproto {
937
1794
  compose(m1, m2) {
938
1795
  return composeMigrations(m1, m2, this.#wasm);
939
1796
  }
1797
+ /**
1798
+ * Compose two lenses into a single lens.
1799
+ *
1800
+ * The resulting lens is equivalent to applying `l1` then `l2`.
1801
+ *
1802
+ * @param l1 - First lens (applied first)
1803
+ * @param l2 - Second lens (applied second)
1804
+ * @returns A new LensHandle representing the composition
1805
+ * @throws {@link import('./types.js').WasmError} if composition fails
1806
+ */
1807
+ composeLenses(l1, l2) {
1808
+ try {
1809
+ const rawHandle = this.#wasm.exports.compose_lenses(
1810
+ l1._handle.id,
1811
+ l2._handle.id
1812
+ );
1813
+ const handle = createHandle(rawHandle, this.#wasm);
1814
+ return new LensHandle(handle, this.#wasm);
1815
+ } catch (error) {
1816
+ throw new WasmError(
1817
+ `compose_lenses failed: ${error instanceof Error ? error.message : String(error)}`,
1818
+ { cause: error }
1819
+ );
1820
+ }
1821
+ }
940
1822
  /**
941
1823
  * Diff two schemas and produce a compatibility report.
942
1824
  *
@@ -951,6 +1833,90 @@ class Panproto {
951
1833
  );
952
1834
  return unpackFromWasm(resultBytes);
953
1835
  }
1836
+ /** Diff two schemas using the full panproto-check engine (20+ change categories). */
1837
+ diffFull(oldSchema, newSchema) {
1838
+ const bytes = this.#wasm.exports.diff_schemas_full(
1839
+ oldSchema._handle.id,
1840
+ newSchema._handle.id
1841
+ );
1842
+ const data = unpackFromWasm(bytes);
1843
+ return new FullDiffReport(data, bytes, this.#wasm);
1844
+ }
1845
+ /** Normalize a schema by collapsing reference chains. Returns a new BuiltSchema. */
1846
+ normalize(schema) {
1847
+ const handle = this.#wasm.exports.normalize_schema(schema._handle.id);
1848
+ return BuiltSchema._fromHandle(handle, schema.data, schema.protocol, this.#wasm);
1849
+ }
1850
+ /** Validate a schema against its protocol's rules. */
1851
+ validateSchema(schema, protocol) {
1852
+ const bytes = this.#wasm.exports.validate_schema(
1853
+ schema._handle.id,
1854
+ protocol._handle.id
1855
+ );
1856
+ const issues = unpackFromWasm(bytes);
1857
+ return new ValidationResult(issues);
1858
+ }
1859
+ /**
1860
+ * Create an I/O protocol registry for parsing and emitting instances.
1861
+ *
1862
+ * The returned registry wraps all 77 built-in protocol codecs and
1863
+ * implements `Disposable` for automatic cleanup.
1864
+ *
1865
+ * @returns A new IoRegistry
1866
+ */
1867
+ io() {
1868
+ const rawHandle = this.#wasm.exports.register_io_protocols();
1869
+ const handle = createHandle(rawHandle, this.#wasm);
1870
+ return new IoRegistry(handle, this.#wasm);
1871
+ }
1872
+ /**
1873
+ * Parse JSON bytes into an Instance.
1874
+ *
1875
+ * Convenience method that wraps `json_to_instance`.
1876
+ *
1877
+ * @param schema - The schema the JSON data conforms to
1878
+ * @param json - JSON bytes or a JSON string
1879
+ * @returns A new Instance
1880
+ */
1881
+ parseJson(schema, json) {
1882
+ const jsonBytes = typeof json === "string" ? new TextEncoder().encode(json) : json;
1883
+ return Instance.fromJson(schema, jsonBytes, this.#wasm);
1884
+ }
1885
+ /**
1886
+ * Convert an Instance to JSON bytes.
1887
+ *
1888
+ * Convenience method that wraps `instance_to_json`.
1889
+ *
1890
+ * @param schema - The schema the instance conforms to
1891
+ * @param instance - The instance to convert
1892
+ * @returns JSON bytes
1893
+ */
1894
+ toJson(schema, instance) {
1895
+ return this.#wasm.exports.instance_to_json(
1896
+ schema._handle.id,
1897
+ instance._bytes
1898
+ );
1899
+ }
1900
+ /**
1901
+ * List all built-in protocol names.
1902
+ *
1903
+ * Returns the names of all 76 built-in protocols supported by the
1904
+ * WASM layer.
1905
+ *
1906
+ * @returns Array of protocol name strings
1907
+ */
1908
+ listProtocols() {
1909
+ return [...getProtocolNames(this.#wasm)];
1910
+ }
1911
+ /**
1912
+ * Initialize an in-memory VCS repository.
1913
+ *
1914
+ * @param protocolName - The protocol name for this repository
1915
+ * @returns A disposable VCS Repository
1916
+ */
1917
+ initRepo(protocolName) {
1918
+ return Repository.init(protocolName, this.#wasm);
1919
+ }
954
1920
  /**
955
1921
  * Release all WASM resources held by this instance.
956
1922
  *
@@ -964,52 +1930,164 @@ class Panproto {
964
1930
  this.#protocols.clear();
965
1931
  }
966
1932
  }
967
- function renameField(oldName, newName) {
968
- return { type: "rename-field", old: oldName, new: newName };
969
- }
970
- function addField(name, vertexKind, defaultValue) {
971
- return { type: "add-field", name, vertexKind, default: defaultValue };
972
- }
973
- function removeField(name) {
974
- return { type: "remove-field", name };
1933
+ class TheoryHandle {
1934
+ #handle;
1935
+ /** @internal Retained for future sort/op inspection methods. */
1936
+ _wasm;
1937
+ constructor(handle, wasm) {
1938
+ this.#handle = handle;
1939
+ this._wasm = wasm;
1940
+ }
1941
+ /** The WASM handle. Internal use only. */
1942
+ get _handle() {
1943
+ return this.#handle;
1944
+ }
1945
+ /** Release the WASM-side theory resource. */
1946
+ [Symbol.dispose]() {
1947
+ this.#handle[Symbol.dispose]();
1948
+ }
975
1949
  }
976
- function wrapInObject(fieldName) {
977
- return { type: "wrap-in-object", fieldName };
1950
+ class TheoryBuilder {
1951
+ #name;
1952
+ #extends;
1953
+ #sorts;
1954
+ #ops;
1955
+ #eqs;
1956
+ constructor(name) {
1957
+ this.#name = name;
1958
+ this.#extends = [];
1959
+ this.#sorts = [];
1960
+ this.#ops = [];
1961
+ this.#eqs = [];
1962
+ }
1963
+ /** Declare that this theory extends a parent theory. */
1964
+ extends(parentName) {
1965
+ this.#extends.push(parentName);
1966
+ return this;
1967
+ }
1968
+ /** Add a simple sort (no parameters). */
1969
+ sort(name) {
1970
+ this.#sorts.push({ name, params: [] });
1971
+ return this;
1972
+ }
1973
+ /** Add a dependent sort with parameters. */
1974
+ dependentSort(name, params) {
1975
+ this.#sorts.push({ name, params });
1976
+ return this;
1977
+ }
1978
+ /** Add an operation. */
1979
+ op(name, inputs, output) {
1980
+ this.#ops.push({ name, inputs, output });
1981
+ return this;
1982
+ }
1983
+ /** Add an equation (axiom). */
1984
+ eq(name, lhs, rhs) {
1985
+ this.#eqs.push({ name, lhs, rhs });
1986
+ return this;
1987
+ }
1988
+ /** Get the theory specification. */
1989
+ toSpec() {
1990
+ return {
1991
+ name: this.#name,
1992
+ extends: this.#extends,
1993
+ sorts: this.#sorts,
1994
+ ops: this.#ops,
1995
+ eqs: this.#eqs
1996
+ };
1997
+ }
1998
+ /**
1999
+ * Build the theory and register it in WASM.
2000
+ *
2001
+ * @param wasm - The WASM module
2002
+ * @returns A disposable TheoryHandle
2003
+ * @throws {@link PanprotoError} if the WASM call fails
2004
+ */
2005
+ build(wasm) {
2006
+ return createTheory(this.toSpec(), wasm);
2007
+ }
978
2008
  }
979
- function hoistField(host, field) {
980
- return { type: "hoist-field", host, field };
2009
+ function createTheory(spec, wasm) {
2010
+ try {
2011
+ const bytes = packToWasm(spec);
2012
+ const rawHandle = wasm.exports.create_theory(bytes);
2013
+ const handle = createHandle(rawHandle, wasm);
2014
+ return new TheoryHandle(handle, wasm);
2015
+ } catch (error) {
2016
+ throw new PanprotoError(
2017
+ `Failed to create theory "${spec.name}": ${error instanceof Error ? error.message : String(error)}`,
2018
+ { cause: error }
2019
+ );
2020
+ }
981
2021
  }
982
- function coerceType(fromKind, toKind) {
983
- return { type: "coerce-type", fromKind, toKind };
2022
+ function colimit(t1, t2, shared, wasm) {
2023
+ try {
2024
+ const rawHandle = wasm.exports.colimit_theories(
2025
+ t1._handle.id,
2026
+ t2._handle.id,
2027
+ shared._handle.id
2028
+ );
2029
+ const handle = createHandle(rawHandle, wasm);
2030
+ return new TheoryHandle(handle, wasm);
2031
+ } catch (error) {
2032
+ throw new WasmError(
2033
+ `colimit_theories failed: ${error instanceof Error ? error.message : String(error)}`,
2034
+ { cause: error }
2035
+ );
2036
+ }
984
2037
  }
985
- function compose(first, second) {
986
- return { type: "compose", first, second };
2038
+ function checkMorphism(morphism, domain, codomain, wasm) {
2039
+ const morphBytes = packToWasm(morphism);
2040
+ const resultBytes = wasm.exports.check_morphism(
2041
+ morphBytes,
2042
+ domain._handle.id,
2043
+ codomain._handle.id
2044
+ );
2045
+ return unpackFromWasm(resultBytes);
987
2046
  }
988
- function pipeline(combinators) {
989
- const [first, ...rest] = combinators;
990
- return rest.reduce((acc, c) => compose(acc, c), first);
2047
+ function migrateModel(sortInterp, morphism, wasm) {
2048
+ const modelBytes = packToWasm(sortInterp);
2049
+ const morphBytes = packToWasm(morphism);
2050
+ const resultBytes = wasm.exports.migrate_model(modelBytes, morphBytes);
2051
+ return unpackFromWasm(resultBytes);
991
2052
  }
992
2053
  exports.ATPROTO_SPEC = ATPROTO_SPEC;
993
2054
  exports.BUILTIN_PROTOCOLS = BUILTIN_PROTOCOLS;
994
2055
  exports.BuiltSchema = BuiltSchema;
2056
+ exports.CompatReport = CompatReport;
995
2057
  exports.CompiledMigration = CompiledMigration;
996
2058
  exports.ExistenceCheckError = ExistenceCheckError;
2059
+ exports.FullDiffReport = FullDiffReport;
997
2060
  exports.GRAPHQL_SPEC = GRAPHQL_SPEC;
2061
+ exports.Instance = Instance;
2062
+ exports.IoRegistry = IoRegistry;
998
2063
  exports.JSON_SCHEMA_SPEC = JSON_SCHEMA_SPEC;
2064
+ exports.LensHandle = LensHandle;
999
2065
  exports.MigrationBuilder = MigrationBuilder;
1000
2066
  exports.MigrationError = MigrationError;
1001
2067
  exports.PROTOBUF_SPEC = PROTOBUF_SPEC;
2068
+ exports.PROTOCOL_CATEGORIES = PROTOCOL_CATEGORIES;
1002
2069
  exports.Panproto = Panproto;
1003
2070
  exports.PanprotoError = PanprotoError;
1004
2071
  exports.Protocol = Protocol;
2072
+ exports.Repository = Repository;
1005
2073
  exports.SQL_SPEC = SQL_SPEC;
1006
2074
  exports.SchemaBuilder = SchemaBuilder;
1007
2075
  exports.SchemaValidationError = SchemaValidationError;
2076
+ exports.TheoryBuilder = TheoryBuilder;
2077
+ exports.TheoryHandle = TheoryHandle;
2078
+ exports.ValidationResult = ValidationResult;
1008
2079
  exports.WasmError = WasmError;
1009
2080
  exports.addField = addField;
2081
+ exports.checkMorphism = checkMorphism;
1010
2082
  exports.coerceType = coerceType;
2083
+ exports.colimit = colimit;
1011
2084
  exports.compose = compose;
2085
+ exports.createTheory = createTheory;
2086
+ exports.fromCombinators = fromCombinators;
2087
+ exports.getBuiltinProtocol = getBuiltinProtocol;
2088
+ exports.getProtocolNames = getProtocolNames;
1012
2089
  exports.hoistField = hoistField;
2090
+ exports.migrateModel = migrateModel;
1013
2091
  exports.pipeline = pipeline;
1014
2092
  exports.removeField = removeField;
1015
2093
  exports.renameField = renameField;