@sixb/core 0.1.18 → 0.1.19
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/README.md +7 -6
- package/dist/actions/commit-edits.d.ts +7 -19
- package/dist/actions/commit-edits.d.ts.map +1 -1
- package/dist/actions/errors.d.ts +0 -3
- package/dist/actions/errors.d.ts.map +1 -1
- package/dist/actions/execution.d.ts +10 -3
- package/dist/actions/execution.d.ts.map +1 -1
- package/dist/actions/index.d.ts +6 -9
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +51 -50
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/request.d.ts +14 -25
- package/dist/actions/request.d.ts.map +1 -1
- package/dist/actions/run/context.d.ts +40 -0
- package/dist/actions/run/context.d.ts.map +1 -0
- package/dist/actions/run/edits-commit.d.ts +31 -0
- package/dist/actions/run/edits-commit.d.ts.map +1 -0
- package/dist/actions/run/effects.d.ts +23 -0
- package/dist/actions/run/effects.d.ts.map +1 -0
- package/dist/actions/run/execute.d.ts +30 -0
- package/dist/actions/run/execute.d.ts.map +1 -0
- package/dist/actions/run/executor.d.ts +95 -0
- package/dist/actions/run/executor.d.ts.map +1 -0
- package/dist/actions/run/normalize.d.ts +36 -0
- package/dist/actions/run/normalize.d.ts.map +1 -0
- package/dist/actions/run/phase-types.d.ts +18 -0
- package/dist/actions/run/phase-types.d.ts.map +1 -0
- package/dist/actions/run/phases.d.ts +20 -0
- package/dist/actions/run/phases.d.ts.map +1 -0
- package/dist/actions/run/run-action.d.ts +25 -0
- package/dist/actions/run/run-action.d.ts.map +1 -0
- package/dist/actions/run/signals.d.ts +67 -0
- package/dist/actions/run/signals.d.ts.map +1 -0
- package/dist/actions/run/state.d.ts +47 -0
- package/dist/actions/run/state.d.ts.map +1 -0
- package/dist/actions/run/types.d.ts +54 -0
- package/dist/actions/run/types.d.ts.map +1 -0
- package/dist/actions/run/writeback.d.ts +17 -0
- package/dist/actions/run/writeback.d.ts.map +1 -0
- package/dist/actions/run-authorization.d.ts +1 -1
- package/dist/actions/run-authorization.d.ts.map +1 -1
- package/dist/actions/run-persistence.d.ts +53 -0
- package/dist/actions/run-persistence.d.ts.map +1 -0
- package/dist/actions/types/context.d.ts +10 -0
- package/dist/actions/types/context.d.ts.map +1 -1
- package/dist/agents/index.js +52 -55
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/streams/index.js +2 -2
- package/dist/agents/streams/internal.js +2 -2
- package/dist/auth/index.js +11 -11
- package/dist/auth/index.js.map +1 -1
- package/dist/authorization/event-visibility.d.ts.map +1 -1
- package/dist/authorization/index.js +3 -3
- package/dist/bootstrap/index.js +46 -49
- package/dist/bootstrap/index.js.map +2 -2
- package/dist/{chunk-d0eb5yy6.js → chunk-1f7wpmyp.js} +5 -5
- package/dist/{chunk-d0eb5yy6.js.map → chunk-1f7wpmyp.js.map} +1 -1
- package/dist/{chunk-vgk3hknm.js → chunk-1k89ecfr.js} +1 -1
- package/dist/{chunk-6h5m9xjx.js → chunk-3gnrh4ve.js} +6 -10
- package/dist/{chunk-6h5m9xjx.js.map → chunk-3gnrh4ve.js.map} +4 -4
- package/dist/{chunk-htr6wjmv.js → chunk-4tcx876c.js} +15 -15
- package/dist/{chunk-htr6wjmv.js.map → chunk-4tcx876c.js.map} +1 -1
- package/dist/{chunk-za5yd58j.js → chunk-52bk93x8.js} +2 -2
- package/dist/{chunk-22v5bpz3.js → chunk-5e43w60z.js} +1 -1
- package/dist/{chunk-zxawp1n2.js → chunk-7kg3k1m9.js} +1 -1
- package/dist/{chunk-ny3xyvcn.js → chunk-7mbzgd9m.js} +3 -3
- package/dist/{chunk-b9affek3.js → chunk-7n440zgt.js} +30 -30
- package/dist/{chunk-b9affek3.js.map → chunk-7n440zgt.js.map} +4 -4
- package/dist/{chunk-kkqwzy6h.js → chunk-7t429e3f.js} +1 -1
- package/dist/{chunk-s4jhg8a3.js → chunk-80mc0971.js} +13 -13
- package/dist/{chunk-s4jhg8a3.js.map → chunk-80mc0971.js.map} +1 -1
- package/dist/{chunk-1b1f2d5f.js → chunk-85yvv5n0.js} +8 -8
- package/dist/{chunk-1b1f2d5f.js.map → chunk-85yvv5n0.js.map} +1 -1
- package/dist/{chunk-6fsr7y93.js → chunk-8mx7qw3y.js} +14 -13
- package/dist/{chunk-6fsr7y93.js.map → chunk-8mx7qw3y.js.map} +4 -4
- package/dist/{chunk-kpx7jrmn.js → chunk-91mg3nj9.js} +59 -59
- package/dist/{chunk-kpx7jrmn.js.map → chunk-91mg3nj9.js.map} +5 -5
- package/dist/{chunk-48e3cn2d.js → chunk-9gyp8hg1.js} +2 -2
- package/dist/{chunk-ewqa58h9.js → chunk-9k93ah50.js} +4 -4
- package/dist/{chunk-ewqa58h9.js.map → chunk-9k93ah50.js.map} +1 -1
- package/dist/{chunk-4wn7bxxa.js → chunk-9ybhtmg4.js} +1 -1
- package/dist/{chunk-fc5zfxy1.js → chunk-a40ad1ft.js} +5 -13
- package/dist/chunk-a40ad1ft.js.map +10 -0
- package/dist/{chunk-vz3gc1tm.js → chunk-aff7cyea.js} +62 -64
- package/dist/{chunk-vz3gc1tm.js.map → chunk-aff7cyea.js.map} +4 -4
- package/dist/{chunk-y2aq6ag9.js → chunk-b38szspf.js} +5 -14
- package/dist/chunk-b38szspf.js.map +11 -0
- package/dist/{chunk-r2y23egw.js → chunk-bkpke1sg.js} +1 -10
- package/dist/chunk-bkpke1sg.js.map +10 -0
- package/dist/{chunk-bs7sqkre.js → chunk-bpgy64fw.js} +1 -1
- package/dist/{chunk-vx2yp45y.js → chunk-c85m1hyj.js} +2 -2
- package/dist/{chunk-v6xpd60t.js → chunk-d2ync1v3.js} +5 -5
- package/dist/{chunk-fe52eczz.js → chunk-ey3pyej1.js} +6345 -2125
- package/dist/chunk-ey3pyej1.js.map +106 -0
- package/dist/{chunk-vjcd8d27.js → chunk-fa02jy15.js} +1 -1
- package/dist/{chunk-y05543x6.js → chunk-hvsb78a1.js} +4 -4
- package/dist/{chunk-y05543x6.js.map → chunk-hvsb78a1.js.map} +1 -1
- package/dist/{chunk-qq90g05n.js → chunk-j2bp5ryw.js} +53 -244
- package/dist/{chunk-qq90g05n.js.map → chunk-j2bp5ryw.js.map} +3 -3
- package/dist/{chunk-qmnnhde6.js → chunk-kg5j6fct.js} +1 -1
- package/dist/{chunk-de4z2bj6.js → chunk-kyzq4wy9.js} +6 -6
- package/dist/{chunk-wr8qp979.js → chunk-m427qgq0.js} +9 -5
- package/dist/{chunk-wr8qp979.js.map → chunk-m427qgq0.js.map} +4 -3
- package/dist/{chunk-e1c18xjx.js → chunk-nq6gpjt7.js} +4 -45
- package/dist/{chunk-e1c18xjx.js.map → chunk-nq6gpjt7.js.map} +5 -5
- package/dist/{chunk-ex496vct.js → chunk-nre82s12.js} +1 -1
- package/dist/{chunk-1hqwggsn.js → chunk-nzapm6p0.js} +1 -2
- package/dist/chunk-nzapm6p0.js.map +12 -0
- package/dist/{chunk-wt4szfec.js → chunk-pbcbn83d.js} +4 -4
- package/dist/{chunk-wt4szfec.js.map → chunk-pbcbn83d.js.map} +1 -1
- package/dist/{chunk-7hpdd6r9.js → chunk-q1vkr2ys.js} +4 -4
- package/dist/{chunk-zb77kjth.js → chunk-rhg02hxq.js} +3 -3
- package/dist/{chunk-y9jmd7e5.js → chunk-s16apyk9.js} +102 -16
- package/dist/chunk-s16apyk9.js.map +16 -0
- package/dist/{chunk-yv9czyfr.js → chunk-t6vq7bff.js} +5 -5
- package/dist/{chunk-qkvz64nd.js → chunk-tjb1mk5t.js} +2 -2
- package/dist/{chunk-p844e0rn.js → chunk-tpn40322.js} +2 -2
- package/dist/{chunk-rt9p8m9t.js → chunk-v9xd5wzd.js} +1 -1
- package/dist/{chunk-2vhg6ysa.js → chunk-vyf9kfqf.js} +1 -1
- package/dist/{chunk-k6hrnf35.js → chunk-wwdfe68s.js} +51 -4
- package/dist/chunk-wwdfe68s.js.map +11 -0
- package/dist/{chunk-f1zxad2t.js → chunk-wz0xs816.js} +6 -6
- package/dist/{chunk-f1zxad2t.js.map → chunk-wz0xs816.js.map} +1 -1
- package/dist/chunk-y0c4e6ex.js +16 -0
- package/dist/chunk-y0c4e6ex.js.map +10 -0
- package/dist/{chunk-qkbx3tf6.js → chunk-y7rqfrgg.js} +6 -13
- package/dist/{chunk-qkbx3tf6.js.map → chunk-y7rqfrgg.js.map} +4 -4
- package/dist/{chunk-dfm066qg.js → chunk-yxd3nnqt.js} +2 -29
- package/dist/chunk-yxd3nnqt.js.map +11 -0
- package/dist/{chunk-70jmg2zx.js → chunk-yzwja0mj.js} +4 -19
- package/dist/{chunk-70jmg2zx.js.map → chunk-yzwja0mj.js.map} +3 -3
- package/dist/{chunk-p0dqwj3n.js → chunk-zrmy8zvw.js} +66 -47
- package/dist/{chunk-p0dqwj3n.js.map → chunk-zrmy8zvw.js.map} +4 -4
- package/dist/deploy/index.d.ts +1 -1
- package/dist/deploy/index.d.ts.map +1 -1
- package/dist/deploy/internal.js +10 -8
- package/dist/deploy/internal.js.map +4 -4
- package/dist/deploy/services.d.ts +1 -2
- package/dist/deploy/services.d.ts.map +1 -1
- package/dist/deploy/types.d.ts +6 -5
- package/dist/deploy/types.d.ts.map +1 -1
- package/dist/deploy/validation.d.ts.map +1 -1
- package/dist/edits/index.js +3 -5
- package/dist/edits/index.js.map +1 -1
- package/dist/error-reporting/internal.js +3 -3
- package/dist/error-reporting/types.d.ts +2 -9
- package/dist/error-reporting/types.d.ts.map +1 -1
- package/dist/errors/catalog.d.ts +12 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/internal.js +1 -1
- package/dist/events/definitions.d.ts +0 -5
- package/dist/events/definitions.d.ts.map +1 -1
- package/dist/events/index.d.ts +3 -3
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +6 -8
- package/dist/events/index.js.map +1 -1
- package/dist/events/scope.d.ts +0 -1
- package/dist/events/scope.d.ts.map +1 -1
- package/dist/events/scope.js +1 -1
- package/dist/events/selectors/builder.d.ts +1 -8
- package/dist/events/selectors/builder.d.ts.map +1 -1
- package/dist/events/selectors/index.d.ts +2 -2
- package/dist/events/selectors/index.d.ts.map +1 -1
- package/dist/events/selectors/index.js +2 -2
- package/dist/events/selectors/predicate.d.ts.map +1 -1
- package/dist/events/selectors/types.d.ts +2 -33
- package/dist/events/selectors/types.d.ts.map +1 -1
- package/dist/events/types/index.d.ts +1 -12
- package/dist/events/types/index.d.ts.map +1 -1
- package/dist/execution/agent.js +41 -44
- package/dist/execution/agent.js.map +1 -1
- package/dist/execution/primitive.js +57 -88
- package/dist/execution/primitive.js.map +3 -4
- package/dist/execution/request.js +35 -38
- package/dist/execution/request.js.map +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +111 -120
- package/dist/index.js.map +1 -1
- package/dist/materialization/index.js +3 -3
- package/dist/materialization/index.js.map +1 -1
- package/dist/materialization/model.d.ts +19 -6
- package/dist/materialization/model.d.ts.map +1 -1
- package/dist/materializer/index.js +31 -31
- package/dist/materializer/index.js.map +1 -1
- package/dist/materializer/shared/normalize.d.ts.map +1 -1
- package/dist/models/execution/index.js +18 -18
- package/dist/objects/action/errors.d.ts +0 -9
- package/dist/objects/action/errors.d.ts.map +1 -1
- package/dist/objects/action/index.d.ts +3 -3
- package/dist/objects/action/index.d.ts.map +1 -1
- package/dist/objects/action/request.d.ts +2 -8
- package/dist/objects/action/request.d.ts.map +1 -1
- package/dist/objects/execution.d.ts +0 -2
- package/dist/objects/execution.d.ts.map +1 -1
- package/dist/objects/index.js +44 -29
- package/dist/objects/index.js.map +1 -1
- package/dist/objects/query/index.js +29 -29
- package/dist/objects/query/index.js.map +1 -1
- package/dist/objects/sdk/object-handle.d.ts.map +1 -1
- package/dist/objects/sdk/object-set.d.ts.map +1 -1
- package/dist/objects/service/index.d.ts +0 -1
- package/dist/objects/service/index.d.ts.map +1 -1
- package/dist/ontology/index.js +5 -5
- package/dist/ontology/index.js.map +1 -1
- package/dist/ontology/internal.js +2 -2
- package/dist/pipelines/index.js +28 -28
- package/dist/projections/internal.js +22 -22
- package/dist/projections/internal.js.map +1 -1
- package/dist/query.js +2 -2
- package/dist/queues/in-memory.d.ts +1 -2
- package/dist/queues/in-memory.d.ts.map +1 -1
- package/dist/queues/index.d.ts +1 -1
- package/dist/queues/index.d.ts.map +1 -1
- package/dist/queues/index.js +1 -1
- package/dist/queues/types.d.ts +0 -7
- package/dist/queues/types.d.ts.map +1 -1
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/internal.js +24 -25
- package/dist/runtime/internal.js.map +1 -1
- package/dist/runtime/resolve-definitions.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +16 -36
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/sandboxes/index.js +5 -5
- package/dist/schedules/builders.d.ts.map +1 -1
- package/dist/schedules/index.js +3 -3
- package/dist/schedules/runtime.d.ts.map +1 -1
- package/dist/schedules/validation.d.ts +0 -1
- package/dist/schedules/validation.d.ts.map +1 -1
- package/dist/security/every.d.ts +1 -1
- package/dist/shares/internal.js +17 -17
- package/dist/storage/action-runs/failure.js +2 -2
- package/dist/storage/action-runs/idempotency.d.ts +3 -4
- package/dist/storage/action-runs/idempotency.d.ts.map +1 -1
- package/dist/storage/action-runs/in-memory.d.ts +2 -9
- package/dist/storage/action-runs/in-memory.d.ts.map +1 -1
- package/dist/storage/action-runs/index.d.ts +1 -3
- package/dist/storage/action-runs/index.d.ts.map +1 -1
- package/dist/storage/action-runs/provider.js +1 -1
- package/dist/storage/action-runs/record.d.ts +20 -0
- package/dist/storage/action-runs/record.d.ts.map +1 -0
- package/dist/storage/action-runs/types.d.ts +39 -68
- package/dist/storage/action-runs/types.d.ts.map +1 -1
- package/dist/storage/ai-limits/enforcement.js +2 -2
- package/dist/storage/connector-connections/provider.js +1 -1
- package/dist/storage/file-upload-sessions/provider.js +9 -9
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +30 -31
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/internal.d.ts +1 -1
- package/dist/storage/internal.d.ts.map +1 -1
- package/dist/storage/internal.js +9 -12
- package/dist/storage/internal.js.map +1 -1
- package/dist/storage/objects/execution-limits.d.ts +1 -1
- package/dist/storage/objects/execution-limits.d.ts.map +1 -1
- package/dist/storage/ontology/provider.js +2 -2
- package/dist/storage/projection-runs/provider.js +2 -2
- package/dist/storage/types.d.ts +1 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/workflow-runs/provider.js +9 -9
- package/dist/syncs/index.js +29 -29
- package/dist/testing/action-execution.d.ts +34 -4
- package/dist/testing/action-execution.d.ts.map +1 -1
- package/dist/testing/action-run-storage-contract.d.ts +11 -0
- package/dist/testing/action-run-storage-contract.d.ts.map +1 -0
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +3202 -2860
- package/dist/testing/index.js.map +7 -6
- package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
- package/dist/testing/queues-contract.d.ts.map +1 -1
- package/dist/webhooks/internal.js +23 -23
- package/dist/webhooks/internal.js.map +1 -1
- package/dist/workers/index.js +1 -1
- package/dist/workflows/index.js +48 -51
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -7
- package/src/actions/commit-edits.ts +9 -37
- package/src/actions/errors.ts +0 -4
- package/src/actions/execution.ts +10 -10
- package/src/actions/index.ts +5 -24
- package/src/actions/request.ts +157 -295
- package/src/actions/run/context.ts +193 -0
- package/src/actions/run/edits-commit.ts +102 -0
- package/src/actions/run/effects.ts +105 -0
- package/src/actions/run/execute.ts +113 -0
- package/src/actions/run/executor.ts +314 -0
- package/src/actions/run/normalize.ts +142 -0
- package/src/actions/run/phase-types.ts +20 -0
- package/src/actions/run/phases.ts +150 -0
- package/src/actions/run/run-action.ts +170 -0
- package/src/actions/run/signals.ts +118 -0
- package/src/actions/run/state.ts +95 -0
- package/src/actions/run/types.ts +61 -0
- package/src/actions/run/writeback.ts +79 -0
- package/src/actions/run-authorization.ts +1 -1
- package/src/actions/run-persistence.ts +105 -0
- package/src/actions/types/context.ts +10 -0
- package/src/authorization/event-visibility.ts +0 -12
- package/src/deploy/index.ts +0 -1
- package/src/deploy/services.ts +2 -11
- package/src/deploy/types.ts +6 -6
- package/src/deploy/validation.ts +12 -6
- package/src/error-reporting/types.ts +2 -10
- package/src/errors/catalog.ts +13 -0
- package/src/events/definitions.ts +0 -17
- package/src/events/index.ts +0 -14
- package/src/events/scope.ts +0 -12
- package/src/events/selectors/builder.ts +0 -51
- package/src/events/selectors/index.ts +0 -6
- package/src/events/selectors/predicate.ts +0 -2
- package/src/events/selectors/types.ts +7 -65
- package/src/events/types/index.ts +0 -11
- package/src/index.ts +1 -19
- package/src/materialization/model.ts +26 -9
- package/src/materializer/README.md +6 -1
- package/src/materializer/edits/commit.ts +42 -32
- package/src/materializer/shared/normalize.ts +25 -14
- package/src/objects/action/errors.ts +0 -12
- package/src/objects/action/index.ts +3 -3
- package/src/objects/action/request.ts +2 -29
- package/src/objects/execution.ts +2 -2
- package/src/objects/sdk/object-handle.ts +3 -29
- package/src/objects/sdk/object-set.ts +5 -30
- package/src/objects/service/index.ts +0 -1
- package/src/queues/in-memory.ts +0 -6
- package/src/queues/index.ts +0 -2
- package/src/queues/types.ts +0 -11
- package/src/runtime/host.ts +5 -0
- package/src/runtime/resolve-definitions.ts +0 -1
- package/src/runtime/types.ts +16 -45
- package/src/schedules/builders.ts +1 -4
- package/src/schedules/runtime.ts +0 -41
- package/src/schedules/validation.ts +2 -16
- package/src/security/every.ts +1 -1
- package/src/storage/action-runs/idempotency.ts +13 -41
- package/src/storage/action-runs/in-memory.ts +20 -311
- package/src/storage/action-runs/index.ts +1 -16
- package/src/storage/action-runs/record.ts +130 -0
- package/src/storage/action-runs/types.ts +37 -92
- package/src/storage/index.ts +1 -9
- package/src/storage/internal.ts +1 -7
- package/src/storage/objects/execution-limits.ts +3 -2
- package/src/storage/types.ts +2 -4
- package/src/testing/action-execution.ts +73 -13
- package/src/testing/action-run-storage-contract.ts +335 -0
- package/src/testing/index.ts +8 -1
- package/src/testing/materializer-storage-contract.ts +174 -18
- package/src/testing/queues-contract.ts +0 -18
- package/dist/actions/run-dispatch.d.ts +0 -25
- package/dist/actions/run-dispatch.d.ts.map +0 -1
- package/dist/actions/worker.d.ts +0 -3
- package/dist/actions/worker.d.ts.map +0 -1
- package/dist/actions/worker.js +0 -325
- package/dist/actions/worker.js.map +0 -11
- package/dist/chunk-1hqwggsn.js.map +0 -12
- package/dist/chunk-85cd7e8c.js +0 -51
- package/dist/chunk-85cd7e8c.js.map +0 -10
- package/dist/chunk-dfm066qg.js.map +0 -11
- package/dist/chunk-fc5zfxy1.js.map +0 -11
- package/dist/chunk-fe52eczz.js.map +0 -49
- package/dist/chunk-fx93hd2n.js +0 -1614
- package/dist/chunk-fx93hd2n.js.map +0 -20
- package/dist/chunk-k6hrnf35.js.map +0 -10
- package/dist/chunk-qxvp5epv.js +0 -1329
- package/dist/chunk-qxvp5epv.js.map +0 -36
- package/dist/chunk-r2y23egw.js.map +0 -10
- package/dist/chunk-tffvfy6s.js +0 -593
- package/dist/chunk-tffvfy6s.js.map +0 -15
- package/dist/chunk-y2aq6ag9.js.map +0 -11
- package/dist/chunk-y9jmd7e5.js.map +0 -15
- package/dist/chunk-zvx7h87b.js +0 -12
- package/dist/chunk-zvx7h87b.js.map +0 -10
- package/dist/events/types/actions.d.ts +0 -39
- package/dist/events/types/actions.d.ts.map +0 -1
- package/dist/objects/service/action-service.d.ts +0 -13
- package/dist/objects/service/action-service.d.ts.map +0 -1
- package/src/actions/run-dispatch.ts +0 -282
- package/src/actions/worker.ts +0 -2
- package/src/events/types/actions.ts +0 -42
- package/src/objects/service/action-service.ts +0 -43
- /package/dist/{chunk-vgk3hknm.js.map → chunk-1k89ecfr.js.map} +0 -0
- /package/dist/{chunk-za5yd58j.js.map → chunk-52bk93x8.js.map} +0 -0
- /package/dist/{chunk-22v5bpz3.js.map → chunk-5e43w60z.js.map} +0 -0
- /package/dist/{chunk-zxawp1n2.js.map → chunk-7kg3k1m9.js.map} +0 -0
- /package/dist/{chunk-ny3xyvcn.js.map → chunk-7mbzgd9m.js.map} +0 -0
- /package/dist/{chunk-kkqwzy6h.js.map → chunk-7t429e3f.js.map} +0 -0
- /package/dist/{chunk-48e3cn2d.js.map → chunk-9gyp8hg1.js.map} +0 -0
- /package/dist/{chunk-4wn7bxxa.js.map → chunk-9ybhtmg4.js.map} +0 -0
- /package/dist/{chunk-bs7sqkre.js.map → chunk-bpgy64fw.js.map} +0 -0
- /package/dist/{chunk-vx2yp45y.js.map → chunk-c85m1hyj.js.map} +0 -0
- /package/dist/{chunk-v6xpd60t.js.map → chunk-d2ync1v3.js.map} +0 -0
- /package/dist/{chunk-vjcd8d27.js.map → chunk-fa02jy15.js.map} +0 -0
- /package/dist/{chunk-qmnnhde6.js.map → chunk-kg5j6fct.js.map} +0 -0
- /package/dist/{chunk-de4z2bj6.js.map → chunk-kyzq4wy9.js.map} +0 -0
- /package/dist/{chunk-ex496vct.js.map → chunk-nre82s12.js.map} +0 -0
- /package/dist/{chunk-7hpdd6r9.js.map → chunk-q1vkr2ys.js.map} +0 -0
- /package/dist/{chunk-zb77kjth.js.map → chunk-rhg02hxq.js.map} +0 -0
- /package/dist/{chunk-yv9czyfr.js.map → chunk-t6vq7bff.js.map} +0 -0
- /package/dist/{chunk-qkvz64nd.js.map → chunk-tjb1mk5t.js.map} +0 -0
- /package/dist/{chunk-p844e0rn.js.map → chunk-tpn40322.js.map} +0 -0
- /package/dist/{chunk-rt9p8m9t.js.map → chunk-v9xd5wzd.js.map} +0 -0
- /package/dist/{chunk-2vhg6ysa.js.map → chunk-vyf9kfqf.js.map} +0 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import type {
|
|
3
|
+
ActionRunFailure,
|
|
4
|
+
ActionRunPhase,
|
|
5
|
+
ActionRunRecord,
|
|
6
|
+
RecordActionRunInput,
|
|
7
|
+
Storage,
|
|
8
|
+
} from "../storage"
|
|
9
|
+
import { ActionRunError } from "../storage"
|
|
10
|
+
import {
|
|
11
|
+
createTestActionExecution,
|
|
12
|
+
createTestActionRunRecord,
|
|
13
|
+
recordTestActionRun,
|
|
14
|
+
type TestActionRunInput,
|
|
15
|
+
} from "./action-execution"
|
|
16
|
+
|
|
17
|
+
export type ActionRunStorageContractStorage = Storage & {
|
|
18
|
+
readonly actionRuns: NonNullable<Storage["actionRuns"]>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ActionRunStorageContractSuiteOptions<
|
|
22
|
+
TStorage extends ActionRunStorageContractStorage = ActionRunStorageContractStorage,
|
|
23
|
+
> {
|
|
24
|
+
readonly createStorage: () => TStorage | Promise<TStorage>
|
|
25
|
+
readonly cleanup?: (storage: TStorage) => void | Promise<void>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const projectId = "action-run-contract"
|
|
29
|
+
const startedAt = new Date("2026-04-29T10:00:00.000Z")
|
|
30
|
+
const finishedAt = new Date("2026-04-29T10:00:03.842Z")
|
|
31
|
+
|
|
32
|
+
function failure<TPhase extends ActionRunPhase>(
|
|
33
|
+
runId: string,
|
|
34
|
+
phase: TPhase,
|
|
35
|
+
message: string
|
|
36
|
+
): ActionRunFailure<TPhase> {
|
|
37
|
+
return {
|
|
38
|
+
code: "action.phase_failed",
|
|
39
|
+
message,
|
|
40
|
+
retryable: false,
|
|
41
|
+
at: finishedAt.toISOString(),
|
|
42
|
+
details: { actionId: "createInvoice", runId, phase },
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type RunOverrides = Partial<
|
|
47
|
+
Pick<
|
|
48
|
+
TestActionRunInput,
|
|
49
|
+
"actionId" | "subject" | "phase" | "startedAt" | "finishedAt" | "writeback"
|
|
50
|
+
>
|
|
51
|
+
> &
|
|
52
|
+
(
|
|
53
|
+
| { readonly status?: "succeeded"; readonly error?: never }
|
|
54
|
+
| { readonly status: "failed"; readonly error: ActionRunFailure }
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
function run(id: string, overrides: RunOverrides = {}): TestActionRunInput {
|
|
58
|
+
return {
|
|
59
|
+
id,
|
|
60
|
+
projectId,
|
|
61
|
+
actionId: "createInvoice",
|
|
62
|
+
subject: { kind: "none" },
|
|
63
|
+
params: { amount: 42 },
|
|
64
|
+
idempotencyKey: `action:${projectId}:${id}`,
|
|
65
|
+
startedAt,
|
|
66
|
+
finishedAt,
|
|
67
|
+
...overrides,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Runs the record-once Action run contract against one complete storage provider. */
|
|
72
|
+
export function runActionRunStorageContractSuite<TStorage extends ActionRunStorageContractStorage>(
|
|
73
|
+
label: string,
|
|
74
|
+
options: ActionRunStorageContractSuiteOptions<TStorage>
|
|
75
|
+
): void {
|
|
76
|
+
const withStorage = async (body: (storage: TStorage) => Promise<void>): Promise<void> => {
|
|
77
|
+
const storage = await options.createStorage()
|
|
78
|
+
try {
|
|
79
|
+
await body(storage)
|
|
80
|
+
} finally {
|
|
81
|
+
await options.cleanup?.(storage)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe(label, () => {
|
|
86
|
+
test("records a terminal run once, and reads it back detached", async () => {
|
|
87
|
+
await withStorage(async (storage) => {
|
|
88
|
+
const input = await createTestActionRunRecord(
|
|
89
|
+
storage.executions,
|
|
90
|
+
run("act_1", {
|
|
91
|
+
subject: { kind: "object", objectTypeId: "Opportunity", primaryId: "opp-1" },
|
|
92
|
+
writeback: {
|
|
93
|
+
status: "succeeded",
|
|
94
|
+
completedAt: new Date("2026-04-29T10:00:01.000Z"),
|
|
95
|
+
result: { externalInvoiceId: "ext_1" },
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
const recorded = await storage.actionRuns.record(input)
|
|
101
|
+
const expected: ActionRunRecord = {
|
|
102
|
+
id: "act_1",
|
|
103
|
+
projectId,
|
|
104
|
+
executionId: "test_action_execution:act_1",
|
|
105
|
+
actionId: "createInvoice",
|
|
106
|
+
subject: { kind: "object", objectTypeId: "Opportunity", primaryId: "opp-1" },
|
|
107
|
+
status: "succeeded",
|
|
108
|
+
phase: "commit",
|
|
109
|
+
startedAt,
|
|
110
|
+
finishedAt,
|
|
111
|
+
params: { amount: 42 },
|
|
112
|
+
idempotencyKey: `action:${projectId}:act_1`,
|
|
113
|
+
writeback: {
|
|
114
|
+
status: "succeeded",
|
|
115
|
+
completedAt: new Date("2026-04-29T10:00:01.000Z"),
|
|
116
|
+
result: { externalInvoiceId: "ext_1" },
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
expect(recorded).toEqual(expected)
|
|
120
|
+
;(recorded.params as { amount: number }).amount = 1
|
|
121
|
+
await expect(storage.actionRuns.getById({ projectId, id: "act_1" })).resolves.toEqual(
|
|
122
|
+
expected
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
await expect(storage.actionRuns.record(input)).rejects.toBeInstanceOf(ActionRunError)
|
|
126
|
+
await expect(
|
|
127
|
+
storage.actionRuns.record({ ...input, id: "act_2", idempotencyKey: "act_2" })
|
|
128
|
+
).rejects.toBeInstanceOf(ActionRunError)
|
|
129
|
+
await expect(storage.actionRuns.getById({ projectId, id: "act_2" })).resolves.toBeNull()
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test("requires the trusted execution created for the run", async () => {
|
|
134
|
+
await withStorage(async (storage) => {
|
|
135
|
+
const input = await createTestActionRunRecord(storage.executions, run("act_owned"))
|
|
136
|
+
await expect(
|
|
137
|
+
storage.actionRuns.record({ ...input, executionId: "missing" })
|
|
138
|
+
).rejects.toThrow("does not authorize Action run")
|
|
139
|
+
|
|
140
|
+
const otherAction = await createTestActionExecution(storage.executions, {
|
|
141
|
+
projectId,
|
|
142
|
+
actionId: "otherAction",
|
|
143
|
+
runId: "act_other",
|
|
144
|
+
})
|
|
145
|
+
await expect(
|
|
146
|
+
storage.actionRuns.record({ ...input, executionId: otherAction })
|
|
147
|
+
).rejects.toThrow("does not authorize Action run")
|
|
148
|
+
await expect(storage.actionRuns.getById({ projectId, id: "act_owned" })).resolves.toBeNull()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test("refuses a record a run cannot end with", async () => {
|
|
153
|
+
await withStorage(async (storage) => {
|
|
154
|
+
const succeeded = await createTestActionRunRecord(storage.executions, run("act_invalid"))
|
|
155
|
+
const writebackFailure = failure("act_invalid", "writeback", "Upstream rejected it")
|
|
156
|
+
const invalid: readonly RecordActionRunInput[] = [
|
|
157
|
+
{ ...succeeded, phase: "effects" },
|
|
158
|
+
{ ...succeeded, status: "failed", phase: "commit", error: writebackFailure },
|
|
159
|
+
{
|
|
160
|
+
...succeeded,
|
|
161
|
+
status: "failed",
|
|
162
|
+
phase: "writeback",
|
|
163
|
+
error: failure("another-run", "writeback", "Upstream rejected it"),
|
|
164
|
+
},
|
|
165
|
+
{ ...succeeded, finishedAt: new Date(startedAt.getTime() - 1) },
|
|
166
|
+
]
|
|
167
|
+
for (const input of invalid) {
|
|
168
|
+
await expect(storage.actionRuns.record(input)).rejects.toThrow()
|
|
169
|
+
}
|
|
170
|
+
await expect(
|
|
171
|
+
storage.actionRuns.getById({ projectId, id: "act_invalid" })
|
|
172
|
+
).resolves.toBeNull()
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test("records a failure with the phase it ended in", async () => {
|
|
177
|
+
await withStorage(async (storage) => {
|
|
178
|
+
const error = failure("act_failed", "writeback", "TeamLeader API returned 503")
|
|
179
|
+
const recorded = await recordTestActionRun(
|
|
180
|
+
storage,
|
|
181
|
+
run("act_failed", {
|
|
182
|
+
status: "failed",
|
|
183
|
+
error,
|
|
184
|
+
writeback: { status: "failed", completedAt: finishedAt, error },
|
|
185
|
+
})
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
expect(recorded).toMatchObject({ status: "failed", phase: "writeback", error })
|
|
189
|
+
await expect(
|
|
190
|
+
storage.actionRuns.getById({ projectId, id: "act_failed" })
|
|
191
|
+
).resolves.toMatchObject({
|
|
192
|
+
status: "failed",
|
|
193
|
+
phase: "writeback",
|
|
194
|
+
error,
|
|
195
|
+
writeback: { status: "failed", error },
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test("records the effects of a run that committed, once", async () => {
|
|
201
|
+
await withStorage(async (storage) => {
|
|
202
|
+
await recordTestActionRun(storage, run("act_effects"))
|
|
203
|
+
const error = failure("act_effects", "effects", "Slack timed out")
|
|
204
|
+
const completedAt = new Date("2026-04-29T10:00:05.000Z")
|
|
205
|
+
|
|
206
|
+
const recorded = await storage.actionRuns.recordEffects({
|
|
207
|
+
id: "act_effects",
|
|
208
|
+
projectId,
|
|
209
|
+
status: "failed",
|
|
210
|
+
completedAt,
|
|
211
|
+
error,
|
|
212
|
+
})
|
|
213
|
+
expect(recorded).toMatchObject({
|
|
214
|
+
status: "succeeded",
|
|
215
|
+
phase: "effects",
|
|
216
|
+
effects: { status: "failed", completedAt, error },
|
|
217
|
+
})
|
|
218
|
+
expect(recorded.error).toBeUndefined()
|
|
219
|
+
|
|
220
|
+
// The same outcome again changes nothing, its completion time included.
|
|
221
|
+
await expect(
|
|
222
|
+
storage.actionRuns.recordEffects({
|
|
223
|
+
id: "act_effects",
|
|
224
|
+
projectId,
|
|
225
|
+
status: "failed",
|
|
226
|
+
completedAt: new Date("2026-04-29T10:00:06.000Z"),
|
|
227
|
+
error,
|
|
228
|
+
})
|
|
229
|
+
).resolves.toEqual(recorded)
|
|
230
|
+
await expect(
|
|
231
|
+
storage.actionRuns.recordEffects({ id: "act_effects", projectId, status: "succeeded" })
|
|
232
|
+
).rejects.toBeInstanceOf(ActionRunError)
|
|
233
|
+
await expect(storage.actionRuns.getById({ projectId, id: "act_effects" })).resolves.toEqual(
|
|
234
|
+
recorded
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
test("records effects only on a succeeded run in its commit phase", async () => {
|
|
240
|
+
await withStorage(async (storage) => {
|
|
241
|
+
await recordTestActionRun(storage, run("act_no_edits", { phase: "writeback" }))
|
|
242
|
+
await recordTestActionRun(
|
|
243
|
+
storage,
|
|
244
|
+
run("act_rejected", {
|
|
245
|
+
status: "failed",
|
|
246
|
+
error: failure("act_rejected", "commit", "Commit failed"),
|
|
247
|
+
})
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
for (const id of ["act_no_edits", "act_rejected", "act_missing"]) {
|
|
251
|
+
await expect(
|
|
252
|
+
storage.actionRuns.recordEffects({ id, projectId, status: "succeeded" })
|
|
253
|
+
).rejects.toBeInstanceOf(ActionRunError)
|
|
254
|
+
}
|
|
255
|
+
await expect(
|
|
256
|
+
storage.actionRuns.getById({ projectId, id: "act_no_edits" })
|
|
257
|
+
).resolves.toMatchObject({ phase: "writeback" })
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test("lists runs with filters, in start order, by page", async () => {
|
|
262
|
+
await withStorage(async (storage) => {
|
|
263
|
+
const at = (minute: number) => new Date(Date.UTC(2026, 3, 29, 10, minute))
|
|
264
|
+
await recordTestActionRun(
|
|
265
|
+
storage,
|
|
266
|
+
run("act_1", {
|
|
267
|
+
subject: { kind: "object", objectTypeId: "Opportunity", primaryId: "opp-1" },
|
|
268
|
+
startedAt: at(0),
|
|
269
|
+
finishedAt: at(1),
|
|
270
|
+
status: "failed",
|
|
271
|
+
error: failure("act_1", "validation", "Invalid amount"),
|
|
272
|
+
})
|
|
273
|
+
)
|
|
274
|
+
await recordTestActionRun(
|
|
275
|
+
storage,
|
|
276
|
+
run("act_2", {
|
|
277
|
+
subject: { kind: "object", objectTypeId: "Opportunity", primaryId: "opp-2" },
|
|
278
|
+
startedAt: at(10),
|
|
279
|
+
finishedAt: at(11),
|
|
280
|
+
})
|
|
281
|
+
)
|
|
282
|
+
await recordTestActionRun(
|
|
283
|
+
storage,
|
|
284
|
+
run("act_3", { actionId: "closeOpportunity", startedAt: at(20), finishedAt: at(21) })
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
const succeeded = await storage.actionRuns.list({
|
|
288
|
+
projectId,
|
|
289
|
+
actionId: "createInvoice",
|
|
290
|
+
statuses: ["succeeded"],
|
|
291
|
+
startedAfter: at(5),
|
|
292
|
+
limit: 1,
|
|
293
|
+
})
|
|
294
|
+
expect(succeeded).toMatchObject({ total: 1, hasMore: false })
|
|
295
|
+
expect(succeeded.runs.map((recorded) => recorded.id)).toEqual(["act_2"])
|
|
296
|
+
|
|
297
|
+
const all = await storage.actionRuns.list({ projectId, order: "asc", limit: 2 })
|
|
298
|
+
expect(all).toMatchObject({ total: 3, hasMore: true })
|
|
299
|
+
expect(all.runs.map((recorded) => recorded.id)).toEqual(["act_1", "act_2"])
|
|
300
|
+
|
|
301
|
+
const latest = await storage.actionRuns.list({ projectId, startedBefore: at(15) })
|
|
302
|
+
expect(latest.runs.map((recorded) => recorded.id)).toEqual(["act_2", "act_1"])
|
|
303
|
+
|
|
304
|
+
const forObject = await storage.actionRuns.list({
|
|
305
|
+
projectId,
|
|
306
|
+
objectTypeId: "Opportunity",
|
|
307
|
+
primaryId: "opp-1",
|
|
308
|
+
})
|
|
309
|
+
expect(forObject.runs.map((recorded) => recorded.id)).toEqual(["act_1"])
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
test("drops a record written in a transaction that rolls back", async () => {
|
|
314
|
+
await withStorage(async (storage) => {
|
|
315
|
+
const input = await createTestActionRunRecord(storage.executions, run("act_rolled_back"))
|
|
316
|
+
await expect(
|
|
317
|
+
storage.transaction(async (tx) => {
|
|
318
|
+
await tx.actionRuns?.record(input)
|
|
319
|
+
throw new Error("rollback")
|
|
320
|
+
})
|
|
321
|
+
).rejects.toThrow("rollback")
|
|
322
|
+
await expect(
|
|
323
|
+
storage.actionRuns.getById({ projectId, id: "act_rolled_back" })
|
|
324
|
+
).resolves.toBeNull()
|
|
325
|
+
|
|
326
|
+
await storage.transaction(async (tx) => {
|
|
327
|
+
await tx.actionRuns?.record(input)
|
|
328
|
+
})
|
|
329
|
+
await expect(
|
|
330
|
+
storage.actionRuns.getById({ projectId, id: "act_rolled_back" })
|
|
331
|
+
).resolves.toMatchObject({ status: "succeeded" })
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
}
|
package/src/testing/index.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
export {
|
|
2
2
|
createTestActionExecution,
|
|
3
|
-
|
|
3
|
+
createTestActionRunRecord,
|
|
4
|
+
recordTestActionRun,
|
|
5
|
+
type TestActionRunInput,
|
|
4
6
|
} from "./action-execution"
|
|
7
|
+
export {
|
|
8
|
+
type ActionRunStorageContractStorage,
|
|
9
|
+
type ActionRunStorageContractSuiteOptions,
|
|
10
|
+
runActionRunStorageContractSuite,
|
|
11
|
+
} from "./action-run-storage-contract"
|
|
5
12
|
export { createTestAgentExecution } from "./agent-execution"
|
|
6
13
|
export {
|
|
7
14
|
type AgentStorageContractSuiteOptions,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import type { TrustedPrimitiveRef } from "../execution"
|
|
14
14
|
import { restoreTrustedPrimitiveExecutionScope } from "../execution/durable"
|
|
15
15
|
import { createTestingScope } from "../execution/scopes"
|
|
16
|
+
import { MaterializationConflictError } from "../materialization/errors"
|
|
16
17
|
import type {
|
|
17
18
|
ExpectedLinkRevision,
|
|
18
19
|
ExpectedObjectRevision,
|
|
@@ -35,8 +36,13 @@ import type {
|
|
|
35
36
|
PlannedReplacementIdentity,
|
|
36
37
|
ProjectionExecution,
|
|
37
38
|
ProjectionRunStorage,
|
|
39
|
+
RecordActionRunInput,
|
|
38
40
|
} from "../storage"
|
|
39
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
createTestActionExecution,
|
|
43
|
+
createTestActionRunRecord,
|
|
44
|
+
recordTestActionRun,
|
|
45
|
+
} from "./action-execution"
|
|
40
46
|
import { createTestProjectionExecution, startTestProjectionRun } from "./projection-execution"
|
|
41
47
|
|
|
42
48
|
export interface MaterializerStorageContractProvider<TStorage extends Storage> {
|
|
@@ -406,23 +412,29 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
406
412
|
})
|
|
407
413
|
).toBeNull()
|
|
408
414
|
|
|
409
|
-
|
|
415
|
+
// A run id is recorded once, so a commit for a run that is already recorded commits nothing.
|
|
416
|
+
const recordedRun = {
|
|
417
|
+
id: "recorded-action-run",
|
|
410
418
|
projectId: "materializer-storage-contract",
|
|
411
419
|
actionId: "renameDevice",
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
420
|
+
subject: { kind: "object", objectTypeId: Device.id, primaryId: "one" },
|
|
421
|
+
params: {},
|
|
422
|
+
idempotencyKey: "recorded-action-run",
|
|
423
|
+
} as const
|
|
424
|
+
await recordTestActionRun(storage, { ...recordedRun, phase: "validation" })
|
|
425
|
+
const recordedActionMaterializer = await primitiveMaterializer(materializer, storage, {
|
|
415
426
|
kind: "action",
|
|
416
427
|
id: "renameDevice",
|
|
417
|
-
runId:
|
|
428
|
+
runId: recordedRun.id,
|
|
418
429
|
})
|
|
419
430
|
await expect(
|
|
420
|
-
|
|
431
|
+
recordedActionMaterializer.edits.commit({
|
|
421
432
|
mode: "atomic",
|
|
422
|
-
source: { kind: "action", actionId: "renameDevice", runId:
|
|
433
|
+
source: { kind: "action", actionId: "renameDevice", runId: recordedRun.id },
|
|
434
|
+
run: await createTestActionRunRecord(storage.executions, recordedRun),
|
|
423
435
|
operations: [
|
|
424
436
|
{
|
|
425
|
-
id: "
|
|
437
|
+
id: "recorded-run-rename",
|
|
426
438
|
kind: "object.patch",
|
|
427
439
|
ref: { objectTypeId: Device.id, primaryId: "one" },
|
|
428
440
|
set: { name: "Must not persist" },
|
|
@@ -434,9 +446,15 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
434
446
|
expectedLinks: [],
|
|
435
447
|
expectedLinkScopes: [],
|
|
436
448
|
})
|
|
437
|
-
).rejects.toThrow("
|
|
449
|
+
).rejects.toThrow("recorded-action-run")
|
|
450
|
+
expect(
|
|
451
|
+
await storage.actionRuns.getById({
|
|
452
|
+
projectId: "materializer-storage-contract",
|
|
453
|
+
id: recordedRun.id,
|
|
454
|
+
})
|
|
455
|
+
).toMatchObject({ phase: "validation" })
|
|
438
456
|
|
|
439
|
-
await
|
|
457
|
+
const actionRun = await createTestActionRunRecord(storage.executions, {
|
|
440
458
|
id: "action-run",
|
|
441
459
|
projectId: "materializer-storage-contract",
|
|
442
460
|
actionId: "renameDevice",
|
|
@@ -444,10 +462,6 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
444
462
|
params: {},
|
|
445
463
|
idempotencyKey: "action-run",
|
|
446
464
|
})
|
|
447
|
-
await storage.actionRuns.start({
|
|
448
|
-
id: "action-run",
|
|
449
|
-
projectId: "materializer-storage-contract",
|
|
450
|
-
})
|
|
451
465
|
const actionMaterializer = await primitiveMaterializer(materializer, storage, {
|
|
452
466
|
kind: "action",
|
|
453
467
|
id: "renameDevice",
|
|
@@ -456,6 +470,7 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
456
470
|
const actionCommit = await actionMaterializer.edits.commit({
|
|
457
471
|
mode: "atomic",
|
|
458
472
|
source: { kind: "action", actionId: "renameDevice", runId: "action-run" },
|
|
473
|
+
run: actionRun,
|
|
459
474
|
operations: [
|
|
460
475
|
{
|
|
461
476
|
id: "rename",
|
|
@@ -470,6 +485,13 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
470
485
|
expectedLinks: [],
|
|
471
486
|
expectedLinkScopes: [],
|
|
472
487
|
})
|
|
488
|
+
// The commit recorded the run it carried, with its edits.
|
|
489
|
+
expect(
|
|
490
|
+
await storage.actionRuns.getById({
|
|
491
|
+
projectId: "materializer-storage-contract",
|
|
492
|
+
id: "action-run",
|
|
493
|
+
})
|
|
494
|
+
).toMatchObject({ status: "succeeded", phase: "commit", executionId: actionRun.executionId })
|
|
473
495
|
expect(
|
|
474
496
|
await storage.ontology.commits.getByOrigin({
|
|
475
497
|
projectId: "materializer-storage-contract",
|
|
@@ -941,7 +963,7 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
941
963
|
})
|
|
942
964
|
)
|
|
943
965
|
const commitObservation = async (runId: string, name: string) => {
|
|
944
|
-
await
|
|
966
|
+
const run = await createTestActionRunRecord(storage.executions, {
|
|
945
967
|
id: runId,
|
|
946
968
|
projectId: "materializer-storage-contract",
|
|
947
969
|
actionId: "observePeers",
|
|
@@ -949,7 +971,6 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
949
971
|
params: {},
|
|
950
972
|
idempotencyKey: runId,
|
|
951
973
|
})
|
|
952
|
-
await storage.actionRuns.start({ id: runId, projectId: "materializer-storage-contract" })
|
|
953
974
|
const actionMaterializer = await primitiveMaterializer(materializer, storage, {
|
|
954
975
|
kind: "action",
|
|
955
976
|
id: "observePeers",
|
|
@@ -958,6 +979,7 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
958
979
|
return actionMaterializer.edits.commit({
|
|
959
980
|
mode: "atomic",
|
|
960
981
|
source: { kind: "action", actionId: "observePeers", runId },
|
|
982
|
+
run,
|
|
961
983
|
operations: [
|
|
962
984
|
{
|
|
963
985
|
id: `record-${runId}`,
|
|
@@ -1000,6 +1022,13 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
1000
1022
|
primaryId: source.primaryId,
|
|
1001
1023
|
})
|
|
1002
1024
|
).toMatchObject({ properties: { name: "observed peers" } })
|
|
1025
|
+
// The conflict fails the commit after it inserted the run's record, which rolls back with it.
|
|
1026
|
+
expect(
|
|
1027
|
+
await storage.actionRuns.getById({
|
|
1028
|
+
projectId: "materializer-storage-contract",
|
|
1029
|
+
id: "stale-many-scope-action",
|
|
1030
|
+
})
|
|
1031
|
+
).toBeNull()
|
|
1003
1032
|
} finally {
|
|
1004
1033
|
await provider.cleanup?.(createdStorage)
|
|
1005
1034
|
}
|
|
@@ -1354,6 +1383,97 @@ export function runMaterializerStorageContractSuite<TStorage extends Storage>(
|
|
|
1354
1383
|
}
|
|
1355
1384
|
})
|
|
1356
1385
|
|
|
1386
|
+
// Two processes can execute one run id, each under an execution of its own, and commit its edits
|
|
1387
|
+
// at the same time: the second commit waits for the first, then replays it and is refused.
|
|
1388
|
+
// Removal proof: drop `recordActionRun` from `executeEditTransaction`
|
|
1389
|
+
// (`materializer/edits/commit.ts`), and no record holds the run that committed.
|
|
1390
|
+
if (provider.concurrentTransactions) {
|
|
1391
|
+
test(`${name} commits a run id once when two of its executions commit together`, async () => {
|
|
1392
|
+
const createdStorage = await provider.createStorage()
|
|
1393
|
+
const storage = requireContractStorage(createdStorage)
|
|
1394
|
+
const primitive = { kind: "action", id: "createDevice", runId: "shared-run" } as const
|
|
1395
|
+
const scopeOf = async (executionId: string) => {
|
|
1396
|
+
const execution = await storage.executions.getById({
|
|
1397
|
+
projectId: CONTRACT_PROJECT,
|
|
1398
|
+
id: executionId,
|
|
1399
|
+
})
|
|
1400
|
+
if (!execution) throw new Error(`Test execution '${executionId}' is missing.`)
|
|
1401
|
+
return restoreTrustedPrimitiveExecutionScope({ execution, primitive })
|
|
1402
|
+
}
|
|
1403
|
+
const commitOf = (run: RecordActionRunInput, primaryId: string) => ({
|
|
1404
|
+
mode: "atomic" as const,
|
|
1405
|
+
source: { kind: "action" as const, actionId: "createDevice", runId: "shared-run" },
|
|
1406
|
+
run,
|
|
1407
|
+
operations: [
|
|
1408
|
+
{
|
|
1409
|
+
id: "create",
|
|
1410
|
+
kind: "object.create" as const,
|
|
1411
|
+
ref: deviceRef(primaryId),
|
|
1412
|
+
properties: { id: primaryId, name: primaryId },
|
|
1413
|
+
},
|
|
1414
|
+
],
|
|
1415
|
+
expectedObjects: [],
|
|
1416
|
+
expectedLinks: [],
|
|
1417
|
+
expectedLinkScopes: [],
|
|
1418
|
+
})
|
|
1419
|
+
try {
|
|
1420
|
+
const firstRun = await createTestActionRunRecord(storage.executions, {
|
|
1421
|
+
id: "shared-run",
|
|
1422
|
+
projectId: CONTRACT_PROJECT,
|
|
1423
|
+
actionId: "createDevice",
|
|
1424
|
+
subject: { kind: "none" },
|
|
1425
|
+
params: {},
|
|
1426
|
+
idempotencyKey: "shared-run",
|
|
1427
|
+
})
|
|
1428
|
+
const secondRun = {
|
|
1429
|
+
...firstRun,
|
|
1430
|
+
executionId: await createTestActionExecution(storage.executions, {
|
|
1431
|
+
projectId: CONTRACT_PROJECT,
|
|
1432
|
+
actionId: "createDevice",
|
|
1433
|
+
runId: "shared-run",
|
|
1434
|
+
executionId: "shared-run-second",
|
|
1435
|
+
}),
|
|
1436
|
+
}
|
|
1437
|
+
const paused = pauseBeforeFinalize(storage)
|
|
1438
|
+
const first = contractMaterializer(paused.storage)
|
|
1439
|
+
.withScope(await scopeOf(firstRun.executionId))
|
|
1440
|
+
.edits.commit(commitOf(firstRun, "first"))
|
|
1441
|
+
// It has recorded the run and holds its transaction open.
|
|
1442
|
+
await paused.reached
|
|
1443
|
+
|
|
1444
|
+
let retries = 0
|
|
1445
|
+
const recording = observeActionRunRecords(storage)
|
|
1446
|
+
const second = contractMaterializer(recording.storage, {}, () => {
|
|
1447
|
+
retries += 1
|
|
1448
|
+
})
|
|
1449
|
+
.withScope(await scopeOf(secondRun.executionId))
|
|
1450
|
+
.edits.commit(commitOf(secondRun, "second"))
|
|
1451
|
+
.then(
|
|
1452
|
+
() => null,
|
|
1453
|
+
(error: unknown) => error
|
|
1454
|
+
)
|
|
1455
|
+
// Its transaction reads before the first one commits, and then records the run too.
|
|
1456
|
+
await recording.reached
|
|
1457
|
+
paused.release()
|
|
1458
|
+
|
|
1459
|
+
await expect(first).resolves.toMatchObject({ created: true })
|
|
1460
|
+
const refused = await second
|
|
1461
|
+
expect(refused).toBeInstanceOf(MaterializationConflictError)
|
|
1462
|
+
expect(refused instanceof MaterializationConflictError ? refused.kind : refused).toBe(
|
|
1463
|
+
"idempotency"
|
|
1464
|
+
)
|
|
1465
|
+
expect(retries).toBeGreaterThan(0)
|
|
1466
|
+
expect(
|
|
1467
|
+
await storage.actionRuns.getById({ projectId: CONTRACT_PROJECT, id: "shared-run" })
|
|
1468
|
+
).toMatchObject({ executionId: firstRun.executionId })
|
|
1469
|
+
expect(await device(storage, "first")).not.toBeNull()
|
|
1470
|
+
expect(await device(storage, "second")).toBeNull()
|
|
1471
|
+
} finally {
|
|
1472
|
+
await provider.cleanup?.(createdStorage)
|
|
1473
|
+
}
|
|
1474
|
+
})
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1357
1477
|
// Removal proof: in PostgreSQL's `committedSince`, count only commits at or past the
|
|
1358
1478
|
// watermark's xmax; the edit, in flight when the plan opened, is then taken as seen.
|
|
1359
1479
|
if (provider.concurrentTransactions) {
|
|
@@ -1907,7 +2027,8 @@ const CONTRACT_PROJECT = "materializer-storage-contract"
|
|
|
1907
2027
|
|
|
1908
2028
|
function contractMaterializer(
|
|
1909
2029
|
storage: Storage,
|
|
1910
|
-
batching: { readonly transactionReplanRows?: number } = {}
|
|
2030
|
+
batching: { readonly transactionReplanRows?: number } = {},
|
|
2031
|
+
onSerializationRetry?: () => void
|
|
1911
2032
|
): OntologyMaterializer {
|
|
1912
2033
|
return createOntologyMaterializer({
|
|
1913
2034
|
projectId: CONTRACT_PROJECT,
|
|
@@ -1916,6 +2037,7 @@ function contractMaterializer(
|
|
|
1916
2037
|
storage,
|
|
1917
2038
|
dependencies: {
|
|
1918
2039
|
batching: { sourceStageRows: 1, statePageRows: 1, planChunkRows: 1, ...batching },
|
|
2040
|
+
...(onSerializationRetry === undefined ? {} : { onSerializationRetry }),
|
|
1919
2041
|
},
|
|
1920
2042
|
})
|
|
1921
2043
|
}
|
|
@@ -1986,6 +2108,40 @@ function observePlanning(
|
|
|
1986
2108
|
}
|
|
1987
2109
|
|
|
1988
2110
|
/** The same storage, whose transactions wait before they finalize until `release` is called. */
|
|
2111
|
+
/** Resolves `reached` when a transaction of `storage` first records an Action run. */
|
|
2112
|
+
function observeActionRunRecords(storage: ContractStorage) {
|
|
2113
|
+
const reached = Promise.withResolvers<void>()
|
|
2114
|
+
const observed = new Proxy(storage, {
|
|
2115
|
+
get(target, property) {
|
|
2116
|
+
if (property !== "transaction") {
|
|
2117
|
+
const value = Reflect.get(target, property, target)
|
|
2118
|
+
return typeof value === "function" ? value.bind(target) : value
|
|
2119
|
+
}
|
|
2120
|
+
return (run: (tx: Storage) => unknown, options?: Parameters<Storage["transaction"]>[1]) =>
|
|
2121
|
+
target.transaction((tx) => {
|
|
2122
|
+
const actionRuns = tx.actionRuns
|
|
2123
|
+
if (!actionRuns) return run(tx)
|
|
2124
|
+
const recording: ActionRunStorage = {
|
|
2125
|
+
record: (input) => {
|
|
2126
|
+
reached.resolve()
|
|
2127
|
+
return actionRuns.record(input)
|
|
2128
|
+
},
|
|
2129
|
+
recordEffects: (input) => actionRuns.recordEffects(input),
|
|
2130
|
+
getById: (input) => actionRuns.getById(input),
|
|
2131
|
+
list: (input) => actionRuns.list(input),
|
|
2132
|
+
}
|
|
2133
|
+
return run(
|
|
2134
|
+
new Proxy(tx, {
|
|
2135
|
+
get: (store, key) =>
|
|
2136
|
+
key === "actionRuns" ? recording : Reflect.get(store, key, store),
|
|
2137
|
+
})
|
|
2138
|
+
)
|
|
2139
|
+
}, options)
|
|
2140
|
+
},
|
|
2141
|
+
})
|
|
2142
|
+
return { storage: observed, reached: reached.promise }
|
|
2143
|
+
}
|
|
2144
|
+
|
|
1989
2145
|
function pauseBeforeFinalize(storage: ContractStorage) {
|
|
1990
2146
|
let reach!: () => void
|
|
1991
2147
|
const reached = new Promise<void>((resolve) => {
|
|
@@ -388,17 +388,6 @@ export function runQueueContractSuite(label: string, options: QueueContractSuite
|
|
|
388
388
|
},
|
|
389
389
|
],
|
|
390
390
|
})
|
|
391
|
-
await queues.actions.enqueue({
|
|
392
|
-
projectId: "project-a",
|
|
393
|
-
jobs: [
|
|
394
|
-
{
|
|
395
|
-
type: "action.run.requested",
|
|
396
|
-
payload: {
|
|
397
|
-
runId: "act_1",
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
],
|
|
401
|
-
})
|
|
402
391
|
await queues.agents.enqueue({
|
|
403
392
|
projectId: "project-a",
|
|
404
393
|
jobs: [
|
|
@@ -433,11 +422,6 @@ export function runQueueContractSuite(label: string, options: QueueContractSuite
|
|
|
433
422
|
workerId: "workflow-worker-1",
|
|
434
423
|
limit: 10,
|
|
435
424
|
})
|
|
436
|
-
const actionLane = await queues.actions.claim({
|
|
437
|
-
projectId: "project-a",
|
|
438
|
-
workerId: "action-worker-1",
|
|
439
|
-
limit: 10,
|
|
440
|
-
})
|
|
441
425
|
const agentLane = await queues.agents.claim({
|
|
442
426
|
projectId: "project-a",
|
|
443
427
|
workerId: "agent-worker-1",
|
|
@@ -460,8 +444,6 @@ export function runQueueContractSuite(label: string, options: QueueContractSuite
|
|
|
460
444
|
expect(projectionLane[0]?.job.payload.runId).toBe("projection-run-1")
|
|
461
445
|
expect(workflowLane).toHaveLength(1)
|
|
462
446
|
expect(workflowLane[0]?.job.payload.runId).toBe("workflow-run-1")
|
|
463
|
-
expect(actionLane).toHaveLength(1)
|
|
464
|
-
expect(actionLane[0]?.job.payload.runId).toBe("act_1")
|
|
465
447
|
expect(agentLane).toHaveLength(1)
|
|
466
448
|
expect(agentLane[0]?.job).toMatchObject({
|
|
467
449
|
type: "agent.run.requested",
|