@panproto/core 0.7.0 → 0.9.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.d.ts CHANGED
@@ -30,6 +30,10 @@ export { Instance } from './instance.js';
30
30
  export { IoRegistry, PROTOCOL_CATEGORIES } from './io.js';
31
31
  export { FullDiffReport, CompatReport, ValidationResult } from './check.js';
32
32
  export type { FullSchemaDiff, CompatReportData, BreakingChange, NonBreakingChange, ConstraintChange, ConstraintDiff, KindChange, SchemaValidationIssue, } from './types.js';
33
+ export { SchemaEnrichment } from './enrichment.js';
34
+ export { ExprBuilder } from './expr.js';
35
+ export { MigrationAnalysis } from './coverage.js';
36
+ export type { ValueKind, SortKind, DirectedEquation, ConflictStrategy, ConflictPolicy, Pattern, Expr, Literal, BuiltinOp, CoverageReport, PartialFailure, PartialReason, OpticKind, EnrichmentSummary, } from './types.js';
33
37
  export type { LawCheckResult, ProtocolSpec, EdgeRule, Vertex, Edge, HyperEdge, Constraint, Variant, RecursionPoint, UsageMode, Span, VertexOptions, EdgeOptions, SchemaData, MigrationSpec, LiftResult, GetResult, DiffReport, SchemaChange, Compatibility, ExistenceReport, ExistenceError, WasmModule, WasmExports, InstanceShape, InstanceValidationResult, } from './types.js';
34
38
  export { TheoryHandle, TheoryBuilder, createTheory, colimit, checkMorphism, migrateModel, factorizeMorphism, } from './gat.js';
35
39
  export type { TheorySpec, TheoryMorphism, Sort, SortParam, GatOperation, Equation, Term, MorphismCheckResult, } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGrE,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAGnB,YAAY,EACV,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5E,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,SAAS,EACT,UAAU,EACV,OAAO,EACP,cAAc,EACd,SAAS,EACT,IAAI,EACJ,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,EACb,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,cAAc,EACd,UAAU,EACV,WAAW,EACX,aAAa,EACb,wBAAwB,GACzB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,OAAO,EACP,aAAa,EACb,YAAY,EACZ,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,UAAU,EACV,cAAc,EACd,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,EACL,aAAa,EACb,SAAS,EACT,qBAAqB,EACrB,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGrE,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAGnB,YAAY,EACV,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5E,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGlD,YAAY,EACV,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,IAAI,EACJ,OAAO,EACP,SAAS,EACT,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,iBAAiB,GAClB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,SAAS,EACT,UAAU,EACV,OAAO,EACP,cAAc,EACd,SAAS,EACT,IAAI,EACJ,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,EACb,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,cAAc,EACd,UAAU,EACV,WAAW,EACX,aAAa,EACb,wBAAwB,GACzB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,OAAO,EACP,aAAa,EACb,YAAY,EACZ,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,UAAU,EACV,cAAc,EACd,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,OAAO,EACL,aAAa,EACb,SAAS,EACT,qBAAqB,EACrB,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -2057,6 +2057,10 @@ class Panproto {
2057
2057
  this.#wasm = wasm;
2058
2058
  this.#protocols = /* @__PURE__ */ new Map();
2059
2059
  }
2060
+ /** The WASM module reference. Internal use only. */
2061
+ get _wasm() {
2062
+ return this.#wasm;
2063
+ }
2060
2064
  /**
2061
2065
  * Initialize the panproto SDK by loading the WASM module.
2062
2066
  *
@@ -2362,6 +2366,535 @@ class Panproto {
2362
2366
  this.#protocols.clear();
2363
2367
  }
2364
2368
  }
2369
+ class SchemaEnrichment {
2370
+ #schema;
2371
+ #defaults;
2372
+ #coercions;
2373
+ #mergers;
2374
+ #policies;
2375
+ constructor(schema, defaults = [], coercions = [], mergers = [], policies = []) {
2376
+ this.#schema = schema;
2377
+ this.#defaults = defaults;
2378
+ this.#coercions = coercions;
2379
+ this.#mergers = mergers;
2380
+ this.#policies = policies;
2381
+ }
2382
+ /**
2383
+ * Add a default expression for a vertex.
2384
+ *
2385
+ * The expression is evaluated when forward migration encounters a
2386
+ * missing value at the given vertex.
2387
+ *
2388
+ * @param vertex - The vertex identifier to attach the default to
2389
+ * @param expr - The default expression
2390
+ * @returns A new enrichment with the default added
2391
+ * @throws {@link PanprotoError} if the vertex is not in the schema
2392
+ */
2393
+ addDefault(vertex, expr) {
2394
+ this.#assertVertex(vertex);
2395
+ if (this.#defaults.some((d) => d.vertex === vertex)) {
2396
+ throw new PanprotoError(
2397
+ `Default already exists for vertex "${vertex}". Remove it first with removeDefault().`
2398
+ );
2399
+ }
2400
+ return new SchemaEnrichment(
2401
+ this.#schema,
2402
+ [...this.#defaults, { vertex, expr }],
2403
+ this.#coercions,
2404
+ this.#mergers,
2405
+ this.#policies
2406
+ );
2407
+ }
2408
+ /**
2409
+ * Add a coercion function between two value kinds.
2410
+ *
2411
+ * The expression defines how to convert values from `fromKind` to
2412
+ * `toKind`. It receives a single argument (the source value) and
2413
+ * must produce a value of the target kind.
2414
+ *
2415
+ * @param fromKind - Source value kind (e.g., 'int')
2416
+ * @param toKind - Target value kind (e.g., 'float')
2417
+ * @param expr - The coercion expression
2418
+ * @returns A new enrichment with the coercion added
2419
+ * @throws {@link PanprotoError} if a coercion for this pair already exists
2420
+ */
2421
+ addCoercion(fromKind, toKind, expr) {
2422
+ if (this.#coercions.some((c) => c.from === fromKind && c.to === toKind)) {
2423
+ throw new PanprotoError(
2424
+ `Coercion from "${fromKind}" to "${toKind}" already exists. Remove it first with removeCoercion().`
2425
+ );
2426
+ }
2427
+ return new SchemaEnrichment(
2428
+ this.#schema,
2429
+ this.#defaults,
2430
+ [...this.#coercions, { from: fromKind, to: toKind, expr }],
2431
+ this.#mergers,
2432
+ this.#policies
2433
+ );
2434
+ }
2435
+ /**
2436
+ * Add a merger expression for a vertex.
2437
+ *
2438
+ * The expression defines how to merge two conflicting values at the
2439
+ * given vertex. It receives two arguments (left and right values)
2440
+ * and must produce a single merged value.
2441
+ *
2442
+ * @param vertex - The vertex identifier to attach the merger to
2443
+ * @param expr - The merger expression
2444
+ * @returns A new enrichment with the merger added
2445
+ * @throws {@link PanprotoError} if the vertex is not in the schema
2446
+ */
2447
+ addMerger(vertex, expr) {
2448
+ this.#assertVertex(vertex);
2449
+ if (this.#mergers.some((m) => m.vertex === vertex)) {
2450
+ throw new PanprotoError(
2451
+ `Merger already exists for vertex "${vertex}". Remove it first.`
2452
+ );
2453
+ }
2454
+ return new SchemaEnrichment(
2455
+ this.#schema,
2456
+ this.#defaults,
2457
+ this.#coercions,
2458
+ [...this.#mergers, { vertex, expr }],
2459
+ this.#policies
2460
+ );
2461
+ }
2462
+ /**
2463
+ * Add a conflict resolution policy for a vertex.
2464
+ *
2465
+ * @param vertex - The vertex identifier
2466
+ * @param strategy - The conflict resolution strategy
2467
+ * @returns A new enrichment with the policy added
2468
+ * @throws {@link PanprotoError} if the vertex is not in the schema
2469
+ */
2470
+ addPolicy(vertex, strategy) {
2471
+ this.#assertVertex(vertex);
2472
+ if (this.#policies.some((p) => p.vertex === vertex)) {
2473
+ throw new PanprotoError(
2474
+ `Policy already exists for vertex "${vertex}". Remove it first.`
2475
+ );
2476
+ }
2477
+ return new SchemaEnrichment(
2478
+ this.#schema,
2479
+ this.#defaults,
2480
+ this.#coercions,
2481
+ this.#mergers,
2482
+ [...this.#policies, { vertex, strategy }]
2483
+ );
2484
+ }
2485
+ /**
2486
+ * Remove the default expression for a vertex.
2487
+ *
2488
+ * @param vertex - The vertex identifier
2489
+ * @returns A new enrichment with the default removed
2490
+ * @throws {@link PanprotoError} if no default exists for the vertex
2491
+ */
2492
+ removeDefault(vertex) {
2493
+ const filtered = this.#defaults.filter((d) => d.vertex !== vertex);
2494
+ if (filtered.length === this.#defaults.length) {
2495
+ throw new PanprotoError(
2496
+ `No default exists for vertex "${vertex}".`
2497
+ );
2498
+ }
2499
+ return new SchemaEnrichment(
2500
+ this.#schema,
2501
+ filtered,
2502
+ this.#coercions,
2503
+ this.#mergers,
2504
+ this.#policies
2505
+ );
2506
+ }
2507
+ /**
2508
+ * Remove the coercion function for a value kind pair.
2509
+ *
2510
+ * @param fromKind - Source value kind
2511
+ * @param toKind - Target value kind
2512
+ * @returns A new enrichment with the coercion removed
2513
+ * @throws {@link PanprotoError} if no coercion exists for the pair
2514
+ */
2515
+ removeCoercion(fromKind, toKind) {
2516
+ const filtered = this.#coercions.filter(
2517
+ (c) => !(c.from === fromKind && c.to === toKind)
2518
+ );
2519
+ if (filtered.length === this.#coercions.length) {
2520
+ throw new PanprotoError(
2521
+ `No coercion exists from "${fromKind}" to "${toKind}".`
2522
+ );
2523
+ }
2524
+ return new SchemaEnrichment(
2525
+ this.#schema,
2526
+ this.#defaults,
2527
+ filtered,
2528
+ this.#mergers,
2529
+ this.#policies
2530
+ );
2531
+ }
2532
+ /**
2533
+ * List all enrichments currently attached.
2534
+ *
2535
+ * @returns An enrichment summary with defaults, coercions, mergers, and policies
2536
+ */
2537
+ listEnrichments() {
2538
+ return {
2539
+ defaults: this.#defaults.map((d) => ({ vertex: d.vertex, expr: d.expr })),
2540
+ coercions: this.#coercions.map((c) => ({ from: c.from, to: c.to, expr: c.expr })),
2541
+ mergers: this.#mergers.map((m) => ({ vertex: m.vertex, expr: m.expr })),
2542
+ policies: this.#policies.map((p) => ({ vertex: p.vertex, strategy: p.strategy }))
2543
+ };
2544
+ }
2545
+ /**
2546
+ * Build the enriched schema.
2547
+ *
2548
+ * Returns a new `BuiltSchema` with the enrichments recorded in the
2549
+ * schema data. The underlying WASM handle is shared with the original
2550
+ * schema (enrichments are metadata that the SDK tracks client-side).
2551
+ *
2552
+ * @returns A new BuiltSchema with enrichment metadata
2553
+ */
2554
+ build() {
2555
+ const originalData = this.#schema.data;
2556
+ const enrichedData = {
2557
+ ...originalData,
2558
+ constraints: {
2559
+ ...originalData.constraints
2560
+ }
2561
+ };
2562
+ const enrichedConstraints = { ...enrichedData.constraints };
2563
+ for (const def of this.#defaults) {
2564
+ const existing = enrichedConstraints[def.vertex] ?? [];
2565
+ enrichedConstraints[def.vertex] = [
2566
+ ...existing,
2567
+ { sort: "__default", value: JSON.stringify(def.expr) }
2568
+ ];
2569
+ }
2570
+ for (const coercion of this.#coercions) {
2571
+ const key = `__coercion:${coercion.from}:${coercion.to}`;
2572
+ const existing = enrichedConstraints[key] ?? [];
2573
+ enrichedConstraints[key] = [
2574
+ ...existing,
2575
+ { sort: "__coercion", value: JSON.stringify(coercion.expr) }
2576
+ ];
2577
+ }
2578
+ for (const merger of this.#mergers) {
2579
+ const existing = enrichedConstraints[merger.vertex] ?? [];
2580
+ enrichedConstraints[merger.vertex] = [
2581
+ ...existing,
2582
+ { sort: "__merger", value: JSON.stringify(merger.expr) }
2583
+ ];
2584
+ }
2585
+ for (const policy of this.#policies) {
2586
+ const existing = enrichedConstraints[policy.vertex] ?? [];
2587
+ enrichedConstraints[policy.vertex] = [
2588
+ ...existing,
2589
+ { sort: "__policy", value: JSON.stringify(policy.strategy) }
2590
+ ];
2591
+ }
2592
+ const enrichedSchemaData = {
2593
+ ...enrichedData,
2594
+ constraints: enrichedConstraints
2595
+ };
2596
+ return new BuiltSchema(
2597
+ this.#schema._handle,
2598
+ enrichedSchemaData,
2599
+ this.#schema._wasm
2600
+ );
2601
+ }
2602
+ /**
2603
+ * Assert that a vertex exists in the schema.
2604
+ *
2605
+ * @param vertex - The vertex to check
2606
+ * @throws {@link PanprotoError} if the vertex is not found
2607
+ */
2608
+ #assertVertex(vertex) {
2609
+ if (!(vertex in this.#schema.vertices)) {
2610
+ throw new PanprotoError(
2611
+ `Vertex "${vertex}" not found in schema. Available vertices: ${Object.keys(this.#schema.vertices).join(", ")}`
2612
+ );
2613
+ }
2614
+ }
2615
+ }
2616
+ class ExprBuilder {
2617
+ /** This class is not instantiable; all methods are static. */
2618
+ constructor() {
2619
+ }
2620
+ /**
2621
+ * Create a variable reference expression.
2622
+ *
2623
+ * @param name - The variable name to reference
2624
+ * @returns A variable expression node
2625
+ */
2626
+ static var_(name) {
2627
+ return { type: "var", name };
2628
+ }
2629
+ /**
2630
+ * Create a literal expression.
2631
+ *
2632
+ * @param value - The literal value
2633
+ * @returns A literal expression node
2634
+ */
2635
+ static lit(value) {
2636
+ return { type: "lit", value };
2637
+ }
2638
+ /**
2639
+ * Create a lambda (anonymous function) expression.
2640
+ *
2641
+ * @param param - The parameter name
2642
+ * @param body - The function body expression
2643
+ * @returns A lambda expression node
2644
+ */
2645
+ static lam(param, body) {
2646
+ return { type: "lam", param, body };
2647
+ }
2648
+ /**
2649
+ * Create a function application expression.
2650
+ *
2651
+ * When multiple arguments are provided, they are applied left-to-right
2652
+ * via currying: `app(f, a, b)` becomes `app(app(f, a), b)`.
2653
+ *
2654
+ * @param func - The function expression
2655
+ * @param args - One or more argument expressions
2656
+ * @returns An application expression node (possibly nested)
2657
+ */
2658
+ static app(func, ...args) {
2659
+ let result = func;
2660
+ for (const arg of args) {
2661
+ result = { type: "app", func: result, arg };
2662
+ }
2663
+ return result;
2664
+ }
2665
+ /**
2666
+ * Create a let-binding expression.
2667
+ *
2668
+ * Binds `value` to `name` in the scope of `body`.
2669
+ *
2670
+ * @param name - The variable name to bind
2671
+ * @param value - The value expression to bind
2672
+ * @param body - The body expression where the binding is in scope
2673
+ * @returns A let expression node
2674
+ */
2675
+ static let_(name, value, body) {
2676
+ return { type: "let", name, value, body };
2677
+ }
2678
+ /**
2679
+ * Create a field access expression.
2680
+ *
2681
+ * @param expr - The record expression to access
2682
+ * @param name - The field name
2683
+ * @returns A field access expression node
2684
+ */
2685
+ static field(expr, name) {
2686
+ return { type: "field", expr, name };
2687
+ }
2688
+ /**
2689
+ * Create a record literal expression.
2690
+ *
2691
+ * @param fields - A mapping of field names to expressions
2692
+ * @returns A record expression node
2693
+ */
2694
+ static record(fields) {
2695
+ const entries = Object.entries(fields);
2696
+ return { type: "record", fields: entries };
2697
+ }
2698
+ /**
2699
+ * Create a list literal expression.
2700
+ *
2701
+ * @param items - The list element expressions
2702
+ * @returns A list expression node
2703
+ */
2704
+ static list(...items) {
2705
+ return { type: "list", items };
2706
+ }
2707
+ /**
2708
+ * Create a pattern-match expression.
2709
+ *
2710
+ * @param scrutinee - The expression to match against
2711
+ * @param arms - Pattern-expression pairs tried in order
2712
+ * @returns A match expression node
2713
+ */
2714
+ static match_(scrutinee, arms) {
2715
+ return { type: "match", scrutinee, arms };
2716
+ }
2717
+ /**
2718
+ * Create a builtin operation expression.
2719
+ *
2720
+ * @param op - The builtin operation name
2721
+ * @param args - Argument expressions for the operation
2722
+ * @returns A builtin expression node
2723
+ */
2724
+ static builtin(op, ...args) {
2725
+ return { type: "builtin", op, args };
2726
+ }
2727
+ /**
2728
+ * Create an index expression for list or record access.
2729
+ *
2730
+ * @param expr - The collection expression
2731
+ * @param index - The index expression
2732
+ * @returns An index expression node
2733
+ */
2734
+ static index(expr, index) {
2735
+ return { type: "index", expr, index };
2736
+ }
2737
+ // -----------------------------------------------------------------
2738
+ // Convenience arithmetic helpers
2739
+ // -----------------------------------------------------------------
2740
+ /**
2741
+ * Add two expressions.
2742
+ *
2743
+ * @param a - Left operand
2744
+ * @param b - Right operand
2745
+ * @returns A builtin 'Add' expression
2746
+ */
2747
+ static add(a, b) {
2748
+ return ExprBuilder.builtin("Add", a, b);
2749
+ }
2750
+ /**
2751
+ * Subtract two expressions.
2752
+ *
2753
+ * @param a - Left operand
2754
+ * @param b - Right operand
2755
+ * @returns A builtin 'Sub' expression
2756
+ */
2757
+ static sub(a, b) {
2758
+ return ExprBuilder.builtin("Sub", a, b);
2759
+ }
2760
+ /**
2761
+ * Multiply two expressions.
2762
+ *
2763
+ * @param a - Left operand
2764
+ * @param b - Right operand
2765
+ * @returns A builtin 'Mul' expression
2766
+ */
2767
+ static mul(a, b) {
2768
+ return ExprBuilder.builtin("Mul", a, b);
2769
+ }
2770
+ /**
2771
+ * Concatenate two expressions (strings or lists).
2772
+ *
2773
+ * @param a - Left operand
2774
+ * @param b - Right operand
2775
+ * @returns A builtin 'Concat' expression
2776
+ */
2777
+ static concat(a, b) {
2778
+ return ExprBuilder.builtin("Concat", a, b);
2779
+ }
2780
+ }
2781
+ function classifyOpticKind(chain, schema, _wasm) {
2782
+ const spec = chain.requirements(schema);
2783
+ const hasDefaults = spec.forwardDefaults.length > 0;
2784
+ const hasCaptured = spec.capturedData.length > 0;
2785
+ if (!hasDefaults && !hasCaptured && spec.kind === "empty") {
2786
+ return "iso";
2787
+ }
2788
+ if (hasDefaults && hasCaptured) {
2789
+ return "affine";
2790
+ }
2791
+ if (hasCaptured) {
2792
+ return "lens";
2793
+ }
2794
+ if (hasDefaults) {
2795
+ return "prism";
2796
+ }
2797
+ return "traversal";
2798
+ }
2799
+ function runDryRun(compiled, instances, srcSchema, _tgtSchema, wasm) {
2800
+ const totalRecords = instances.length;
2801
+ const failed = [];
2802
+ let successful = 0;
2803
+ let recordIndex = 0;
2804
+ for (const record of instances) {
2805
+ try {
2806
+ const inputBytes = packToWasm(record);
2807
+ const instanceBytes = wasm.exports.json_to_instance(
2808
+ srcSchema._handle.id,
2809
+ inputBytes
2810
+ );
2811
+ wasm.exports.lift_record(compiled._handle.id, instanceBytes);
2812
+ successful++;
2813
+ } catch (error) {
2814
+ const reason = categorizeFailure(error);
2815
+ failed.push({ recordId: recordIndex, reason });
2816
+ }
2817
+ recordIndex++;
2818
+ }
2819
+ const coverageRatio = totalRecords > 0 ? successful / totalRecords : 1;
2820
+ return {
2821
+ totalRecords,
2822
+ successful,
2823
+ failed,
2824
+ coverageRatio
2825
+ };
2826
+ }
2827
+ function categorizeFailure(error) {
2828
+ const message = error instanceof Error ? error.message : String(error);
2829
+ if (message.includes("constraint") || message.includes("Constraint")) {
2830
+ const constraintMatch = /constraint\s+"?([^"]+)"?\s+violated.*?value\s+"?([^"]*)"?/i.exec(message);
2831
+ return {
2832
+ type: "constraint_violation",
2833
+ constraint: constraintMatch?.[1] ?? "unknown",
2834
+ value: constraintMatch?.[2] ?? "unknown"
2835
+ };
2836
+ }
2837
+ if (message.includes("required") || message.includes("missing")) {
2838
+ const fieldMatch = /(?:required|missing)\s+(?:field\s+)?"?([^"]+)"?/i.exec(message);
2839
+ return {
2840
+ type: "missing_required_field",
2841
+ field: fieldMatch?.[1] ?? "unknown"
2842
+ };
2843
+ }
2844
+ if (message.includes("type") && message.includes("mismatch")) {
2845
+ const typeMatch = /expected\s+"?([^"]+)"?\s+got\s+"?([^"]+)"?/i.exec(message);
2846
+ return {
2847
+ type: "type_mismatch",
2848
+ expected: typeMatch?.[1] ?? "unknown",
2849
+ got: typeMatch?.[2] ?? "unknown"
2850
+ };
2851
+ }
2852
+ return {
2853
+ type: "expr_eval_failed",
2854
+ exprName: "migration",
2855
+ error: message
2856
+ };
2857
+ }
2858
+ class MigrationAnalysis {
2859
+ #wasm;
2860
+ /**
2861
+ * Create a new migration analysis instance.
2862
+ *
2863
+ * @param panproto - The Panproto instance providing WASM access
2864
+ */
2865
+ constructor(panproto) {
2866
+ this.#wasm = panproto._wasm;
2867
+ }
2868
+ /**
2869
+ * Run a dry-run migration and return a coverage report.
2870
+ *
2871
+ * Tests each instance record against the compiled migration without
2872
+ * persisting results, producing detailed failure information for
2873
+ * records that cannot be migrated.
2874
+ *
2875
+ * @param compiled - The compiled migration to test
2876
+ * @param instances - Array of instance records (plain objects)
2877
+ * @param srcSchema - The source schema the instances conform to
2878
+ * @param tgtSchema - The target schema
2879
+ * @returns A coverage report with per-record success/failure data
2880
+ */
2881
+ dryRun(compiled, instances, srcSchema, tgtSchema) {
2882
+ return runDryRun(compiled, instances, srcSchema, tgtSchema, this.#wasm);
2883
+ }
2884
+ /**
2885
+ * Classify the optic kind of a protolens chain.
2886
+ *
2887
+ * Determines whether the chain represents an isomorphism, lens, prism,
2888
+ * affine transformation, or traversal based on its complement structure.
2889
+ *
2890
+ * @param chain - The protolens chain to classify
2891
+ * @param schema - The schema to check the chain against
2892
+ * @returns The optic kind classification
2893
+ */
2894
+ opticKind(chain, schema) {
2895
+ return classifyOpticKind(chain, schema, this.#wasm);
2896
+ }
2897
+ }
2365
2898
  class TheoryHandle {
2366
2899
  #handle;
2367
2900
  /** @internal Retained for future sort/op inspection methods. */
@@ -2501,12 +3034,14 @@ export {
2501
3034
  CompiledMigration,
2502
3035
  DataSetHandle,
2503
3036
  ExistenceCheckError,
3037
+ ExprBuilder,
2504
3038
  FullDiffReport,
2505
3039
  GRAPHQL_SPEC,
2506
3040
  Instance,
2507
3041
  IoRegistry,
2508
3042
  JSON_SCHEMA_SPEC,
2509
3043
  LensHandle,
3044
+ MigrationAnalysis,
2510
3045
  MigrationBuilder,
2511
3046
  MigrationError,
2512
3047
  PROTOBUF_SPEC,
@@ -2518,6 +3053,7 @@ export {
2518
3053
  Repository,
2519
3054
  SQL_SPEC,
2520
3055
  SchemaBuilder,
3056
+ SchemaEnrichment,
2521
3057
  SchemaValidationError,
2522
3058
  SymmetricLensHandle,
2523
3059
  TheoryBuilder,