@earendil-works/chord 1.4.0 → 1.5.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/CHANGELOG.md +41 -0
- package/README.md +79 -14
- package/dist/cell.d.ts +7 -7
- package/dist/cell.d.ts.map +1 -1
- package/dist/cell.js.map +1 -1
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +1 -1
- package/dist/context/index.js.map +1 -1
- package/dist/contract.d.ts +51 -52
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +19 -24
- package/dist/contract.js.map +1 -1
- package/dist/delta/apply-immutable-trusted.js +3 -4
- package/dist/delta/apply-immutable-trusted.js.map +1 -1
- package/dist/delta/diff.d.ts.map +1 -1
- package/dist/delta/diff.js +5 -3
- package/dist/delta/diff.js.map +1 -1
- package/dist/delta/index.d.ts +9 -1
- package/dist/delta/index.d.ts.map +1 -1
- package/dist/delta/index.js +15 -11
- package/dist/delta/index.js.map +1 -1
- package/dist/delta/pending.d.ts +36 -0
- package/dist/delta/pending.d.ts.map +1 -0
- package/dist/delta/pending.js +143 -0
- package/dist/delta/pending.js.map +1 -0
- package/dist/delta/tracker.d.ts +12 -0
- package/dist/delta/tracker.d.ts.map +1 -1
- package/dist/delta/tracker.js +109 -39
- package/dist/delta/tracker.js.map +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +9 -7
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/next/contracts/accepts.d.ts +81 -0
- package/dist/next/contracts/accepts.d.ts.map +1 -0
- package/dist/next/contracts/accepts.js +123 -0
- package/dist/next/contracts/accepts.js.map +1 -0
- package/dist/next/contracts/contract.d.ts +121 -0
- package/dist/next/contracts/contract.d.ts.map +1 -0
- package/dist/next/contracts/contract.js +50 -0
- package/dist/next/contracts/contract.js.map +1 -0
- package/dist/next/contracts/declare.d.ts +107 -0
- package/dist/next/contracts/declare.d.ts.map +1 -0
- package/dist/next/contracts/declare.js +136 -0
- package/dist/next/contracts/declare.js.map +1 -0
- package/dist/next/contracts/evolve.d.ts +92 -0
- package/dist/next/contracts/evolve.d.ts.map +1 -0
- package/dist/next/contracts/evolve.js +179 -0
- package/dist/next/contracts/evolve.js.map +1 -0
- package/dist/next/contracts/fingerprint.d.ts +16 -0
- package/dist/next/contracts/fingerprint.d.ts.map +1 -0
- package/dist/next/contracts/fingerprint.js +50 -0
- package/dist/next/contracts/fingerprint.js.map +1 -0
- package/dist/next/contracts/history.d.ts +71 -0
- package/dist/next/contracts/history.d.ts.map +1 -0
- package/dist/next/contracts/history.js +3 -0
- package/dist/next/contracts/history.js.map +1 -0
- package/dist/next/contracts/host-kind.d.ts +40 -0
- package/dist/next/contracts/host-kind.d.ts.map +1 -0
- package/dist/next/contracts/host-kind.js +37 -0
- package/dist/next/contracts/host-kind.js.map +1 -0
- package/dist/next/contracts/index.d.ts +7 -0
- package/dist/next/contracts/index.d.ts.map +1 -0
- package/dist/next/contracts/index.js +6 -0
- package/dist/next/contracts/index.js.map +1 -0
- package/dist/next/contracts/internal.d.ts +10 -0
- package/dist/next/contracts/internal.d.ts.map +1 -0
- package/dist/next/contracts/internal.js +9 -0
- package/dist/next/contracts/internal.js.map +1 -0
- package/dist/next/contracts/names.d.ts +3 -0
- package/dist/next/contracts/names.d.ts.map +1 -0
- package/dist/next/contracts/names.js +13 -0
- package/dist/next/contracts/names.js.map +1 -0
- package/dist/next/contracts/params.d.ts +28 -0
- package/dist/next/contracts/params.d.ts.map +1 -0
- package/dist/next/contracts/params.js +50 -0
- package/dist/next/contracts/params.js.map +1 -0
- package/dist/next/contracts/schema.d.ts +102 -0
- package/dist/next/contracts/schema.d.ts.map +1 -0
- package/dist/next/contracts/schema.js +103 -0
- package/dist/next/contracts/schema.js.map +1 -0
- package/dist/next/contracts/snapshot.d.ts +16 -0
- package/dist/next/contracts/snapshot.d.ts.map +1 -0
- package/dist/next/contracts/snapshot.js +167 -0
- package/dist/next/contracts/snapshot.js.map +1 -0
- package/dist/next/data/args.d.ts +50 -0
- package/dist/next/data/args.d.ts.map +1 -0
- package/dist/next/data/args.js +83 -0
- package/dist/next/data/args.js.map +1 -0
- package/dist/next/data/errors.d.ts +73 -0
- package/dist/next/data/errors.d.ts.map +1 -0
- package/dist/next/data/errors.js +107 -0
- package/dist/next/data/errors.js.map +1 -0
- package/dist/next/data/failure.d.ts +26 -0
- package/dist/next/data/failure.d.ts.map +1 -0
- package/dist/next/data/failure.js +72 -0
- package/dist/next/data/failure.js.map +1 -0
- package/dist/next/data/index.d.ts +4 -0
- package/dist/next/data/index.d.ts.map +1 -0
- package/dist/next/data/index.js +4 -0
- package/dist/next/data/index.js.map +1 -0
- package/dist/next/data/internal.d.ts +7 -0
- package/dist/next/data/internal.d.ts.map +1 -0
- package/dist/next/data/internal.js +8 -0
- package/dist/next/data/internal.js.map +1 -0
- package/dist/next/data/params.d.ts +20 -0
- package/dist/next/data/params.d.ts.map +1 -0
- package/dist/next/data/params.js +31 -0
- package/dist/next/data/params.js.map +1 -0
- package/dist/next/data/principal.d.ts +17 -0
- package/dist/next/data/principal.d.ts.map +1 -0
- package/dist/next/data/principal.js +41 -0
- package/dist/next/data/principal.js.map +1 -0
- package/dist/next/data/validator.d.ts +12 -0
- package/dist/next/data/validator.d.ts.map +1 -0
- package/dist/next/data/validator.js +34 -0
- package/dist/next/data/validator.js.map +1 -0
- package/dist/next/documents/address-source.d.ts +20 -0
- package/dist/next/documents/address-source.d.ts.map +1 -0
- package/dist/next/documents/address-source.js +44 -0
- package/dist/next/documents/address-source.js.map +1 -0
- package/dist/next/documents/definition.d.ts +84 -0
- package/dist/next/documents/definition.d.ts.map +1 -0
- package/dist/next/documents/definition.js +73 -0
- package/dist/next/documents/definition.js.map +1 -0
- package/dist/next/documents/documents.d.ts +106 -0
- package/dist/next/documents/documents.d.ts.map +1 -0
- package/dist/next/documents/documents.js +224 -0
- package/dist/next/documents/documents.js.map +1 -0
- package/dist/next/documents/fence.d.ts +9 -0
- package/dist/next/documents/fence.d.ts.map +1 -0
- package/dist/next/documents/fence.js +15 -0
- package/dist/next/documents/fence.js.map +1 -0
- package/dist/next/documents/handles.d.ts +76 -0
- package/dist/next/documents/handles.d.ts.map +1 -0
- package/dist/next/documents/handles.js +66 -0
- package/dist/next/documents/handles.js.map +1 -0
- package/dist/next/documents/index.d.ts +4 -0
- package/dist/next/documents/index.d.ts.map +1 -0
- package/dist/next/documents/index.js +3 -0
- package/dist/next/documents/index.js.map +1 -0
- package/dist/next/documents/internal.d.ts +9 -0
- package/dist/next/documents/internal.d.ts.map +1 -0
- package/dist/next/documents/internal.js +9 -0
- package/dist/next/documents/internal.js.map +1 -0
- package/dist/next/documents/keyed-slot.d.ts +103 -0
- package/dist/next/documents/keyed-slot.d.ts.map +1 -0
- package/dist/next/documents/keyed-slot.js +395 -0
- package/dist/next/documents/keyed-slot.js.map +1 -0
- package/dist/next/documents/line.d.ts +7 -0
- package/dist/next/documents/line.d.ts.map +1 -0
- package/dist/next/documents/line.js +14 -0
- package/dist/next/documents/line.js.map +1 -0
- package/dist/next/documents/persistence.d.ts +104 -0
- package/dist/next/documents/persistence.d.ts.map +1 -0
- package/dist/next/documents/persistence.js +128 -0
- package/dist/next/documents/persistence.js.map +1 -0
- package/dist/next/documents/residency.d.ts +31 -0
- package/dist/next/documents/residency.d.ts.map +1 -0
- package/dist/next/documents/residency.js +93 -0
- package/dist/next/documents/residency.js.map +1 -0
- package/dist/next/documents/slot.d.ts +109 -0
- package/dist/next/documents/slot.d.ts.map +1 -0
- package/dist/next/documents/slot.js +181 -0
- package/dist/next/documents/slot.js.map +1 -0
- package/dist/next/documents/transaction.d.ts +133 -0
- package/dist/next/documents/transaction.d.ts.map +1 -0
- package/dist/next/documents/transaction.js +409 -0
- package/dist/next/documents/transaction.js.map +1 -0
- package/dist/next/host/activity.d.ts +31 -0
- package/dist/next/host/activity.d.ts.map +1 -0
- package/dist/next/host/activity.js +40 -0
- package/dist/next/host/activity.js.map +1 -0
- package/dist/next/host/adapted.d.ts +49 -0
- package/dist/next/host/adapted.d.ts.map +1 -0
- package/dist/next/host/adapted.js +214 -0
- package/dist/next/host/adapted.js.map +1 -0
- package/dist/next/host/attempt.d.ts +278 -0
- package/dist/next/host/attempt.d.ts.map +1 -0
- package/dist/next/host/attempt.js +1072 -0
- package/dist/next/host/attempt.js.map +1 -0
- package/dist/next/host/backoff.d.ts +29 -0
- package/dist/next/host/backoff.d.ts.map +1 -0
- package/dist/next/host/backoff.js +69 -0
- package/dist/next/host/backoff.js.map +1 -0
- package/dist/next/host/ctx.d.ts +47 -0
- package/dist/next/host/ctx.d.ts.map +1 -0
- package/dist/next/host/ctx.js +108 -0
- package/dist/next/host/ctx.js.map +1 -0
- package/dist/next/host/deadline.d.ts +12 -0
- package/dist/next/host/deadline.d.ts.map +1 -0
- package/dist/next/host/deadline.js +40 -0
- package/dist/next/host/deadline.js.map +1 -0
- package/dist/next/host/dependencies.d.ts +65 -0
- package/dist/next/host/dependencies.d.ts.map +1 -0
- package/dist/next/host/dependencies.js +115 -0
- package/dist/next/host/dependencies.js.map +1 -0
- package/dist/next/host/destination.d.ts +48 -0
- package/dist/next/host/destination.d.ts.map +1 -0
- package/dist/next/host/destination.js +101 -0
- package/dist/next/host/destination.js.map +1 -0
- package/dist/next/host/facet.d.ts +79 -0
- package/dist/next/host/facet.d.ts.map +1 -0
- package/dist/next/host/facet.js +55 -0
- package/dist/next/host/facet.js.map +1 -0
- package/dist/next/host/follow.d.ts +52 -0
- package/dist/next/host/follow.d.ts.map +1 -0
- package/dist/next/host/follow.js +207 -0
- package/dist/next/host/follow.js.map +1 -0
- package/dist/next/host/host.d.ts +495 -0
- package/dist/next/host/host.d.ts.map +1 -0
- package/dist/next/host/host.js +1075 -0
- package/dist/next/host/host.js.map +1 -0
- package/dist/next/host/index.d.ts +17 -0
- package/dist/next/host/index.d.ts.map +1 -0
- package/dist/next/host/index.js +6 -0
- package/dist/next/host/index.js.map +1 -0
- package/dist/next/host/internal.d.ts +10 -0
- package/dist/next/host/internal.d.ts.map +1 -0
- package/dist/next/host/internal.js +8 -0
- package/dist/next/host/internal.js.map +1 -0
- package/dist/next/host/members.d.ts +50 -0
- package/dist/next/host/members.d.ts.map +1 -0
- package/dist/next/host/members.js +107 -0
- package/dist/next/host/members.js.map +1 -0
- package/dist/next/host/native.d.ts +55 -0
- package/dist/next/host/native.d.ts.map +1 -0
- package/dist/next/host/native.js +92 -0
- package/dist/next/host/native.js.map +1 -0
- package/dist/next/host/object.d.ts +290 -0
- package/dist/next/host/object.d.ts.map +1 -0
- package/dist/next/host/object.js +1127 -0
- package/dist/next/host/object.js.map +1 -0
- package/dist/next/host/parked.d.ts +23 -0
- package/dist/next/host/parked.d.ts.map +1 -0
- package/dist/next/host/parked.js +79 -0
- package/dist/next/host/parked.js.map +1 -0
- package/dist/next/host/presence.d.ts +34 -0
- package/dist/next/host/presence.d.ts.map +1 -0
- package/dist/next/host/presence.js +117 -0
- package/dist/next/host/presence.js.map +1 -0
- package/dist/next/host/ref-state.d.ts +53 -0
- package/dist/next/host/ref-state.d.ts.map +1 -0
- package/dist/next/host/ref-state.js +83 -0
- package/dist/next/host/ref-state.js.map +1 -0
- package/dist/next/host/reference-keys.d.ts +36 -0
- package/dist/next/host/reference-keys.d.ts.map +1 -0
- package/dist/next/host/reference-keys.js +168 -0
- package/dist/next/host/reference-keys.js.map +1 -0
- package/dist/next/host/reference-source.d.ts +65 -0
- package/dist/next/host/reference-source.d.ts.map +1 -0
- package/dist/next/host/reference-source.js +285 -0
- package/dist/next/host/reference-source.js.map +1 -0
- package/dist/next/host/reference.d.ts +308 -0
- package/dist/next/host/reference.d.ts.map +1 -0
- package/dist/next/host/reference.js +775 -0
- package/dist/next/host/reference.js.map +1 -0
- package/dist/next/host/retry-on.d.ts +28 -0
- package/dist/next/host/retry-on.d.ts.map +1 -0
- package/dist/next/host/retry-on.js +52 -0
- package/dist/next/host/retry-on.js.map +1 -0
- package/dist/next/host/revocation.d.ts +16 -0
- package/dist/next/host/revocation.d.ts.map +1 -0
- package/dist/next/host/revocation.js +27 -0
- package/dist/next/host/revocation.js.map +1 -0
- package/dist/next/host/root.d.ts +27 -0
- package/dist/next/host/root.d.ts.map +1 -0
- package/dist/next/host/root.js +68 -0
- package/dist/next/host/root.js.map +1 -0
- package/dist/next/host/route.d.ts +64 -0
- package/dist/next/host/route.d.ts.map +1 -0
- package/dist/next/host/route.js +190 -0
- package/dist/next/host/route.js.map +1 -0
- package/dist/next/host/routines.d.ts +76 -0
- package/dist/next/host/routines.d.ts.map +1 -0
- package/dist/next/host/routines.js +350 -0
- package/dist/next/host/routines.js.map +1 -0
- package/dist/next/host/sleep.d.ts +9 -0
- package/dist/next/host/sleep.d.ts.map +1 -0
- package/dist/next/host/sleep.js +40 -0
- package/dist/next/host/sleep.js.map +1 -0
- package/dist/next/host/staging.d.ts +94 -0
- package/dist/next/host/staging.d.ts.map +1 -0
- package/dist/next/host/staging.js +302 -0
- package/dist/next/host/staging.js.map +1 -0
- package/dist/next/host/unbound.d.ts +30 -0
- package/dist/next/host/unbound.d.ts.map +1 -0
- package/dist/next/host/unbound.js +67 -0
- package/dist/next/host/unbound.js.map +1 -0
- package/dist/next/host/uuid.d.ts +3 -0
- package/dist/next/host/uuid.d.ts.map +1 -0
- package/dist/next/host/uuid.js +38 -0
- package/dist/next/host/uuid.js.map +1 -0
- package/dist/next/index.d.ts +28 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +18 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/jobs/definition.d.ts +96 -0
- package/dist/next/jobs/definition.d.ts.map +1 -0
- package/dist/next/jobs/definition.js +83 -0
- package/dist/next/jobs/definition.js.map +1 -0
- package/dist/next/jobs/index.d.ts +5 -0
- package/dist/next/jobs/index.d.ts.map +1 -0
- package/dist/next/jobs/index.js +3 -0
- package/dist/next/jobs/index.js.map +1 -0
- package/dist/next/jobs/internal.d.ts +3 -0
- package/dist/next/jobs/internal.d.ts.map +1 -0
- package/dist/next/jobs/internal.js +4 -0
- package/dist/next/jobs/internal.js.map +1 -0
- package/dist/next/jobs/job.d.ts +152 -0
- package/dist/next/jobs/job.d.ts.map +1 -0
- package/dist/next/jobs/job.js +320 -0
- package/dist/next/jobs/job.js.map +1 -0
- package/dist/next/jobs/persisted.d.ts +38 -0
- package/dist/next/jobs/persisted.d.ts.map +1 -0
- package/dist/next/jobs/persisted.js +95 -0
- package/dist/next/jobs/persisted.js.map +1 -0
- package/dist/next/jobs/records.d.ts +231 -0
- package/dist/next/jobs/records.d.ts.map +1 -0
- package/dist/next/jobs/records.js +869 -0
- package/dist/next/jobs/records.js.map +1 -0
- package/dist/next/jobs/run-states.d.ts +69 -0
- package/dist/next/jobs/run-states.d.ts.map +1 -0
- package/dist/next/jobs/run-states.js +228 -0
- package/dist/next/jobs/run-states.js.map +1 -0
- package/dist/next/jobs/run.d.ts +113 -0
- package/dist/next/jobs/run.d.ts.map +1 -0
- package/dist/next/jobs/run.js +200 -0
- package/dist/next/jobs/run.js.map +1 -0
- package/dist/next/jobs/running.d.ts +44 -0
- package/dist/next/jobs/running.d.ts.map +1 -0
- package/dist/next/jobs/running.js +117 -0
- package/dist/next/jobs/running.js.map +1 -0
- package/dist/next/jobs/stored.d.ts +63 -0
- package/dist/next/jobs/stored.d.ts.map +1 -0
- package/dist/next/jobs/stored.js +55 -0
- package/dist/next/jobs/stored.js.map +1 -0
- package/dist/next/link/binary.d.ts +4 -0
- package/dist/next/link/binary.d.ts.map +1 -0
- package/dist/next/link/binary.js +534 -0
- package/dist/next/link/binary.js.map +1 -0
- package/dist/next/link/channel.d.ts +63 -0
- package/dist/next/link/channel.d.ts.map +1 -0
- package/dist/next/link/channel.js +202 -0
- package/dist/next/link/channel.js.map +1 -0
- package/dist/next/link/errors.d.ts +7 -0
- package/dist/next/link/errors.d.ts.map +1 -0
- package/dist/next/link/errors.js +21 -0
- package/dist/next/link/errors.js.map +1 -0
- package/dist/next/link/index.d.ts +8 -0
- package/dist/next/link/index.d.ts.map +1 -0
- package/dist/next/link/index.js +8 -0
- package/dist/next/link/index.js.map +1 -0
- package/dist/next/link/internal.d.ts +9 -0
- package/dist/next/link/internal.d.ts.map +1 -0
- package/dist/next/link/internal.js +10 -0
- package/dist/next/link/internal.js.map +1 -0
- package/dist/next/link/ipc.d.ts +22 -0
- package/dist/next/link/ipc.d.ts.map +1 -0
- package/dist/next/link/ipc.js +64 -0
- package/dist/next/link/ipc.js.map +1 -0
- package/dist/next/link/link.d.ts +34 -0
- package/dist/next/link/link.d.ts.map +1 -0
- package/dist/next/link/link.js +87 -0
- package/dist/next/link/link.js.map +1 -0
- package/dist/next/link/message.d.ts +249 -0
- package/dist/next/link/message.d.ts.map +1 -0
- package/dist/next/link/message.js +123 -0
- package/dist/next/link/message.js.map +1 -0
- package/dist/next/link/part.d.ts +80 -0
- package/dist/next/link/part.d.ts.map +1 -0
- package/dist/next/link/part.js +164 -0
- package/dist/next/link/part.js.map +1 -0
- package/dist/next/link/text.d.ts +6 -0
- package/dist/next/link/text.d.ts.map +1 -0
- package/dist/next/link/text.js +335 -0
- package/dist/next/link/text.js.map +1 -0
- package/dist/next/link/values.d.ts +40 -0
- package/dist/next/link/values.d.ts.map +1 -0
- package/dist/next/link/values.js +117 -0
- package/dist/next/link/values.js.map +1 -0
- package/dist/next/link/websocket.d.ts +26 -0
- package/dist/next/link/websocket.d.ts.map +1 -0
- package/dist/next/link/websocket.js +91 -0
- package/dist/next/link/websocket.js.map +1 -0
- package/dist/next/registries/contribution.d.ts +50 -0
- package/dist/next/registries/contribution.d.ts.map +1 -0
- package/dist/next/registries/contribution.js +129 -0
- package/dist/next/registries/contribution.js.map +1 -0
- package/dist/next/registries/entries.d.ts +18 -0
- package/dist/next/registries/entries.d.ts.map +1 -0
- package/dist/next/registries/entries.js +42 -0
- package/dist/next/registries/entries.js.map +1 -0
- package/dist/next/registries/index.d.ts +3 -0
- package/dist/next/registries/index.d.ts.map +1 -0
- package/dist/next/registries/index.js +5 -0
- package/dist/next/registries/index.js.map +1 -0
- package/dist/next/registries/internal.d.ts +4 -0
- package/dist/next/registries/internal.d.ts.map +1 -0
- package/dist/next/registries/internal.js +5 -0
- package/dist/next/registries/internal.js.map +1 -0
- package/dist/next/registries/record.d.ts +94 -0
- package/dist/next/registries/record.d.ts.map +1 -0
- package/dist/next/registries/record.js +253 -0
- package/dist/next/registries/record.js.map +1 -0
- package/dist/next/registries/registry.d.ts +74 -0
- package/dist/next/registries/registry.d.ts.map +1 -0
- package/dist/next/registries/registry.js +14 -0
- package/dist/next/registries/registry.js.map +1 -0
- package/dist/next/registries/value.d.ts +48 -0
- package/dist/next/registries/value.d.ts.map +1 -0
- package/dist/next/registries/value.js +199 -0
- package/dist/next/registries/value.js.map +1 -0
- package/dist/next/remote/calls.d.ts +25 -0
- package/dist/next/remote/calls.d.ts.map +1 -0
- package/dist/next/remote/calls.js +62 -0
- package/dist/next/remote/calls.js.map +1 -0
- package/dist/next/remote/consumer.d.ts +42 -0
- package/dist/next/remote/consumer.d.ts.map +1 -0
- package/dist/next/remote/consumer.js +343 -0
- package/dist/next/remote/consumer.js.map +1 -0
- package/dist/next/remote/hold.d.ts +130 -0
- package/dist/next/remote/hold.d.ts.map +1 -0
- package/dist/next/remote/hold.js +537 -0
- package/dist/next/remote/hold.js.map +1 -0
- package/dist/next/remote/host-link.d.ts +77 -0
- package/dist/next/remote/host-link.d.ts.map +1 -0
- package/dist/next/remote/host-link.js +182 -0
- package/dist/next/remote/host-link.js.map +1 -0
- package/dist/next/remote/internal.d.ts +6 -0
- package/dist/next/remote/internal.d.ts.map +1 -0
- package/dist/next/remote/internal.js +6 -0
- package/dist/next/remote/internal.js.map +1 -0
- package/dist/next/remote/path.d.ts +46 -0
- package/dist/next/remote/path.d.ts.map +1 -0
- package/dist/next/remote/path.js +73 -0
- package/dist/next/remote/path.js.map +1 -0
- package/dist/next/remote/provider.d.ts +76 -0
- package/dist/next/remote/provider.d.ts.map +1 -0
- package/dist/next/remote/provider.js +506 -0
- package/dist/next/remote/provider.js.map +1 -0
- package/dist/next/remote/relay.d.ts +58 -0
- package/dist/next/remote/relay.d.ts.map +1 -0
- package/dist/next/remote/relay.js +117 -0
- package/dist/next/remote/relay.js.map +1 -0
- package/dist/next/remote/remote-object.d.ts +133 -0
- package/dist/next/remote/remote-object.d.ts.map +1 -0
- package/dist/next/remote/remote-object.js +411 -0
- package/dist/next/remote/remote-object.js.map +1 -0
- package/dist/next/remote/replica.d.ts +59 -0
- package/dist/next/remote/replica.d.ts.map +1 -0
- package/dist/next/remote/replica.js +125 -0
- package/dist/next/remote/replica.js.map +1 -0
- package/dist/next/state/batch.d.ts +64 -0
- package/dist/next/state/batch.d.ts.map +1 -0
- package/dist/next/state/batch.js +163 -0
- package/dist/next/state/batch.js.map +1 -0
- package/dist/next/state/derive.d.ts +52 -0
- package/dist/next/state/derive.d.ts.map +1 -0
- package/dist/next/state/derive.js +182 -0
- package/dist/next/state/derive.js.map +1 -0
- package/dist/next/state/index.d.ts +6 -0
- package/dist/next/state/index.d.ts.map +1 -0
- package/dist/next/state/index.js +4 -0
- package/dist/next/state/index.js.map +1 -0
- package/dist/next/state/internal.d.ts +6 -0
- package/dist/next/state/internal.d.ts.map +1 -0
- package/dist/next/state/internal.js +8 -0
- package/dist/next/state/internal.js.map +1 -0
- package/dist/next/state/native-source.d.ts +106 -0
- package/dist/next/state/native-source.d.ts.map +1 -0
- package/dist/next/state/native-source.js +240 -0
- package/dist/next/state/native-source.js.map +1 -0
- package/dist/next/state/watchable.d.ts +114 -0
- package/dist/next/state/watchable.d.ts.map +1 -0
- package/dist/next/state/watchable.js +156 -0
- package/dist/next/state/watchable.js.map +1 -0
- package/dist/next/state/writable.d.ts +32 -0
- package/dist/next/state/writable.d.ts.map +1 -0
- package/dist/next/state/writable.js +49 -0
- package/dist/next/state/writable.js.map +1 -0
- package/dist/next/store/adapter.d.ts +67 -0
- package/dist/next/store/adapter.d.ts.map +1 -0
- package/dist/next/store/adapter.js +118 -0
- package/dist/next/store/adapter.js.map +1 -0
- package/dist/next/store/allocator.d.ts +17 -0
- package/dist/next/store/allocator.d.ts.map +1 -0
- package/dist/next/store/allocator.js +30 -0
- package/dist/next/store/allocator.js.map +1 -0
- package/dist/next/store/applier.d.ts +8 -0
- package/dist/next/store/applier.d.ts.map +1 -0
- package/dist/next/store/applier.js +375 -0
- package/dist/next/store/applier.js.map +1 -0
- package/dist/next/store/apply-batch.d.ts +12 -0
- package/dist/next/store/apply-batch.d.ts.map +1 -0
- package/dist/next/store/apply-batch.js +83 -0
- package/dist/next/store/apply-batch.js.map +1 -0
- package/dist/next/store/archive.d.ts +32 -0
- package/dist/next/store/archive.d.ts.map +1 -0
- package/dist/next/store/archive.js +129 -0
- package/dist/next/store/archive.js.map +1 -0
- package/dist/next/store/backend.d.ts +10 -0
- package/dist/next/store/backend.d.ts.map +1 -0
- package/dist/next/store/backend.js +15 -0
- package/dist/next/store/backend.js.map +1 -0
- package/dist/next/store/catalog.d.ts +24 -0
- package/dist/next/store/catalog.d.ts.map +1 -0
- package/dist/next/store/catalog.js +17 -0
- package/dist/next/store/catalog.js.map +1 -0
- package/dist/next/store/claims.d.ts +23 -0
- package/dist/next/store/claims.d.ts.map +1 -0
- package/dist/next/store/claims.js +65 -0
- package/dist/next/store/claims.js.map +1 -0
- package/dist/next/store/columns.d.ts +77 -0
- package/dist/next/store/columns.d.ts.map +1 -0
- package/dist/next/store/columns.js +77 -0
- package/dist/next/store/columns.js.map +1 -0
- package/dist/next/store/engine.d.ts +176 -0
- package/dist/next/store/engine.d.ts.map +1 -0
- package/dist/next/store/engine.js +283 -0
- package/dist/next/store/engine.js.map +1 -0
- package/dist/next/store/evaluate.d.ts +17 -0
- package/dist/next/store/evaluate.d.ts.map +1 -0
- package/dist/next/store/evaluate.js +128 -0
- package/dist/next/store/evaluate.js.map +1 -0
- package/dist/next/store/export.d.ts +17 -0
- package/dist/next/store/export.d.ts.map +1 -0
- package/dist/next/store/export.js +200 -0
- package/dist/next/store/export.js.map +1 -0
- package/dist/next/store/expression.d.ts +150 -0
- package/dist/next/store/expression.d.ts.map +1 -0
- package/dist/next/store/expression.js +268 -0
- package/dist/next/store/expression.js.map +1 -0
- package/dist/next/store/import.d.ts +10 -0
- package/dist/next/store/import.d.ts.map +1 -0
- package/dist/next/store/import.js +276 -0
- package/dist/next/store/import.js.map +1 -0
- package/dist/next/store/index.d.ts +16 -0
- package/dist/next/store/index.d.ts.map +1 -0
- package/dist/next/store/index.js +13 -0
- package/dist/next/store/index.js.map +1 -0
- package/dist/next/store/json-column.d.ts +33 -0
- package/dist/next/store/json-column.d.ts.map +1 -0
- package/dist/next/store/json-column.js +61 -0
- package/dist/next/store/json-column.js.map +1 -0
- package/dist/next/store/jsonl.d.ts +9 -0
- package/dist/next/store/jsonl.d.ts.map +1 -0
- package/dist/next/store/jsonl.js +61 -0
- package/dist/next/store/jsonl.js.map +1 -0
- package/dist/next/store/outbox.d.ts +26 -0
- package/dist/next/store/outbox.d.ts.map +1 -0
- package/dist/next/store/outbox.js +80 -0
- package/dist/next/store/outbox.js.map +1 -0
- package/dist/next/store/query.d.ts +39 -0
- package/dist/next/store/query.d.ts.map +1 -0
- package/dist/next/store/query.js +97 -0
- package/dist/next/store/query.js.map +1 -0
- package/dist/next/store/refusal.d.ts +15 -0
- package/dist/next/store/refusal.d.ts.map +1 -0
- package/dist/next/store/refusal.js +36 -0
- package/dist/next/store/refusal.js.map +1 -0
- package/dist/next/store/rows.d.ts +80 -0
- package/dist/next/store/rows.d.ts.map +1 -0
- package/dist/next/store/rows.js +183 -0
- package/dist/next/store/rows.js.map +1 -0
- package/dist/next/store/schema.d.ts +25 -0
- package/dist/next/store/schema.d.ts.map +1 -0
- package/dist/next/store/schema.js +115 -0
- package/dist/next/store/schema.js.map +1 -0
- package/dist/next/store/sql.d.ts +73 -0
- package/dist/next/store/sql.d.ts.map +1 -0
- package/dist/next/store/sql.js +234 -0
- package/dist/next/store/sql.js.map +1 -0
- package/dist/next/store/step.d.ts +48 -0
- package/dist/next/store/step.d.ts.map +1 -0
- package/dist/next/store/step.js +50 -0
- package/dist/next/store/step.js.map +1 -0
- package/dist/next/store/store.d.ts +161 -0
- package/dist/next/store/store.d.ts.map +1 -0
- package/dist/next/store/store.js +627 -0
- package/dist/next/store/store.js.map +1 -0
- package/dist/next/store/table-changes.d.ts +29 -0
- package/dist/next/store/table-changes.d.ts.map +1 -0
- package/dist/next/store/table-changes.js +102 -0
- package/dist/next/store/table-changes.js.map +1 -0
- package/dist/next/store/table-evolve.d.ts +74 -0
- package/dist/next/store/table-evolve.d.ts.map +1 -0
- package/dist/next/store/table-evolve.js +201 -0
- package/dist/next/store/table-evolve.js.map +1 -0
- package/dist/next/store/table-handle.d.ts +67 -0
- package/dist/next/store/table-handle.d.ts.map +1 -0
- package/dist/next/store/table-handle.js +101 -0
- package/dist/next/store/table-handle.js.map +1 -0
- package/dist/next/store/table-history.d.ts +75 -0
- package/dist/next/store/table-history.d.ts.map +1 -0
- package/dist/next/store/table-history.js +42 -0
- package/dist/next/store/table-history.js.map +1 -0
- package/dist/next/store/table-migration.d.ts +16 -0
- package/dist/next/store/table-migration.d.ts.map +1 -0
- package/dist/next/store/table-migration.js +202 -0
- package/dist/next/store/table-migration.js.map +1 -0
- package/dist/next/store/table-writes.d.ts +125 -0
- package/dist/next/store/table-writes.d.ts.map +1 -0
- package/dist/next/store/table-writes.js +165 -0
- package/dist/next/store/table-writes.js.map +1 -0
- package/dist/next/store/table.d.ts +80 -0
- package/dist/next/store/table.d.ts.map +1 -0
- package/dist/next/store/table.js +103 -0
- package/dist/next/store/table.js.map +1 -0
- package/dist/next/store/text.d.ts +14 -0
- package/dist/next/store/text.d.ts.map +1 -0
- package/dist/next/store/text.js +51 -0
- package/dist/next/store/text.js.map +1 -0
- package/dist/next/store/writes.d.ts +5 -0
- package/dist/next/store/writes.d.ts.map +1 -0
- package/dist/next/store/writes.js +4 -0
- package/dist/next/store/writes.js.map +1 -0
- package/dist/next-node/compression.d.ts +7 -0
- package/dist/next-node/compression.d.ts.map +1 -0
- package/dist/next-node/compression.js +24 -0
- package/dist/next-node/compression.js.map +1 -0
- package/dist/next-node/http/admission.d.ts +63 -0
- package/dist/next-node/http/admission.d.ts.map +1 -0
- package/dist/next-node/http/admission.js +134 -0
- package/dist/next-node/http/admission.js.map +1 -0
- package/dist/next-node/http/authenticate.d.ts +21 -0
- package/dist/next-node/http/authenticate.d.ts.map +1 -0
- package/dist/next-node/http/authenticate.js +34 -0
- package/dist/next-node/http/authenticate.js.map +1 -0
- package/dist/next-node/http/cookies.d.ts +24 -0
- package/dist/next-node/http/cookies.d.ts.map +1 -0
- package/dist/next-node/http/cookies.js +62 -0
- package/dist/next-node/http/cookies.js.map +1 -0
- package/dist/next-node/http/file.d.ts +23 -0
- package/dist/next-node/http/file.d.ts.map +1 -0
- package/dist/next-node/http/file.js +94 -0
- package/dist/next-node/http/file.js.map +1 -0
- package/dist/next-node/http/headers.d.ts +37 -0
- package/dist/next-node/http/headers.d.ts.map +1 -0
- package/dist/next-node/http/headers.js +129 -0
- package/dist/next-node/http/headers.js.map +1 -0
- package/dist/next-node/http/http.d.ts +100 -0
- package/dist/next-node/http/http.d.ts.map +1 -0
- package/dist/next-node/http/http.js +462 -0
- package/dist/next-node/http/http.js.map +1 -0
- package/dist/next-node/http/incoming.d.ts +19 -0
- package/dist/next-node/http/incoming.d.ts.map +1 -0
- package/dist/next-node/http/incoming.js +31 -0
- package/dist/next-node/http/incoming.js.map +1 -0
- package/dist/next-node/http/index.d.ts +11 -0
- package/dist/next-node/http/index.d.ts.map +1 -0
- package/dist/next-node/http/index.js +12 -0
- package/dist/next-node/http/index.js.map +1 -0
- package/dist/next-node/http/node.d.ts +78 -0
- package/dist/next-node/http/node.d.ts.map +1 -0
- package/dist/next-node/http/node.js +291 -0
- package/dist/next-node/http/node.js.map +1 -0
- package/dist/next-node/http/request.d.ts +54 -0
- package/dist/next-node/http/request.d.ts.map +1 -0
- package/dist/next-node/http/request.js +116 -0
- package/dist/next-node/http/request.js.map +1 -0
- package/dist/next-node/http/response.d.ts +63 -0
- package/dist/next-node/http/response.d.ts.map +1 -0
- package/dist/next-node/http/response.js +202 -0
- package/dist/next-node/http/response.js.map +1 -0
- package/dist/next-node/http/router.d.ts +114 -0
- package/dist/next-node/http/router.d.ts.map +1 -0
- package/dist/next-node/http/router.js +261 -0
- package/dist/next-node/http/router.js.map +1 -0
- package/dist/next-node/http/upgrades.d.ts +58 -0
- package/dist/next-node/http/upgrades.d.ts.map +1 -0
- package/dist/next-node/http/upgrades.js +184 -0
- package/dist/next-node/http/upgrades.js.map +1 -0
- package/dist/next-node/plugins/build.d.ts +57 -0
- package/dist/next-node/plugins/build.d.ts.map +1 -0
- package/dist/next-node/plugins/build.js +334 -0
- package/dist/next-node/plugins/build.js.map +1 -0
- package/dist/next-node/plugins/discover.d.ts +36 -0
- package/dist/next-node/plugins/discover.d.ts.map +1 -0
- package/dist/next-node/plugins/discover.js +91 -0
- package/dist/next-node/plugins/discover.js.map +1 -0
- package/dist/next-node/plugins/evaluate.d.ts +33 -0
- package/dist/next-node/plugins/evaluate.d.ts.map +1 -0
- package/dist/next-node/plugins/evaluate.js +98 -0
- package/dist/next-node/plugins/evaluate.js.map +1 -0
- package/dist/next-node/plugins/index.d.ts +9 -0
- package/dist/next-node/plugins/index.d.ts.map +1 -0
- package/dist/next-node/plugins/index.js +11 -0
- package/dist/next-node/plugins/index.js.map +1 -0
- package/dist/next-node/plugins/install.d.ts +13 -0
- package/dist/next-node/plugins/install.d.ts.map +1 -0
- package/dist/next-node/plugins/install.js +37 -0
- package/dist/next-node/plugins/install.js.map +1 -0
- package/dist/next-node/plugins/plugins.d.ts +135 -0
- package/dist/next-node/plugins/plugins.d.ts.map +1 -0
- package/dist/next-node/plugins/plugins.js +206 -0
- package/dist/next-node/plugins/plugins.js.map +1 -0
- package/dist/next-node/plugins/runtime.d.ts +41 -0
- package/dist/next-node/plugins/runtime.d.ts.map +1 -0
- package/dist/next-node/plugins/runtime.js +136 -0
- package/dist/next-node/plugins/runtime.js.map +1 -0
- package/dist/next-node/plugins/stack.d.ts +16 -0
- package/dist/next-node/plugins/stack.d.ts.map +1 -0
- package/dist/next-node/plugins/stack.js +61 -0
- package/dist/next-node/plugins/stack.js.map +1 -0
- package/dist/next-node/plugins/watcher.d.ts +58 -0
- package/dist/next-node/plugins/watcher.d.ts.map +1 -0
- package/dist/next-node/plugins/watcher.js +281 -0
- package/dist/next-node/plugins/watcher.js.map +1 -0
- package/dist/next-node/workers/control.d.ts +83 -0
- package/dist/next-node/workers/control.d.ts.map +1 -0
- package/dist/next-node/workers/control.js +77 -0
- package/dist/next-node/workers/control.js.map +1 -0
- package/dist/next-node/workers/index.d.ts +5 -0
- package/dist/next-node/workers/index.d.ts.map +1 -0
- package/dist/next-node/workers/index.js +7 -0
- package/dist/next-node/workers/index.js.map +1 -0
- package/dist/next-node/workers/lock.d.ts +21 -0
- package/dist/next-node/workers/lock.d.ts.map +1 -0
- package/dist/next-node/workers/lock.js +137 -0
- package/dist/next-node/workers/lock.js.map +1 -0
- package/dist/next-node/workers/main.d.ts +46 -0
- package/dist/next-node/workers/main.d.ts.map +1 -0
- package/dist/next-node/workers/main.js +230 -0
- package/dist/next-node/workers/main.js.map +1 -0
- package/dist/next-node/workers/process.d.ts +42 -0
- package/dist/next-node/workers/process.d.ts.map +1 -0
- package/dist/next-node/workers/process.js +125 -0
- package/dist/next-node/workers/process.js.map +1 -0
- package/dist/next-node/workers/workers.d.ts +80 -0
- package/dist/next-node/workers/workers.d.ts.map +1 -0
- package/dist/next-node/workers/workers.js +572 -0
- package/dist/next-node/workers/workers.js.map +1 -0
- package/dist/object.d.ts +4 -4
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +3 -4
- package/dist/object.js.map +1 -1
- package/dist/output.d.ts +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +4 -2
- package/dist/output.js.map +1 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +2 -2
- package/dist/remote.js.map +1 -1
- package/dist/schema.d.ts +8 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +180 -5
- package/dist/schema.js.map +1 -1
- package/dist/sqlite/cloudflare.d.ts +2 -0
- package/dist/sqlite/cloudflare.d.ts.map +1 -1
- package/dist/sqlite/cloudflare.js +10 -0
- package/dist/sqlite/cloudflare.js.map +1 -1
- package/dist/sqlite/database.d.ts +5 -2
- package/dist/sqlite/database.d.ts.map +1 -1
- package/dist/sqlite/database.js.map +1 -1
- package/dist/sqlite/node.d.ts +4 -1
- package/dist/sqlite/node.d.ts.map +1 -1
- package/dist/sqlite/node.js +22 -5
- package/dist/sqlite/node.js.map +1 -1
- package/dist/watch.d.ts +194 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +383 -0
- package/dist/watch.js.map +1 -0
- package/dist/wire.d.ts +174 -193
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +81 -80
- package/dist/wire.js.map +1 -1
- package/docs/guide.md +15 -15
- package/docs/next/benchmarks.md +959 -0
- package/docs/next/decisions.md +2957 -0
- package/docs/next/dependencies/book.md +1671 -0
- package/docs/next/dependencies/index.html +2641 -0
- package/docs/next/dependencies/model.md +330 -0
- package/docs/next/design.md +2554 -0
- package/docs/next/durable-observers/book.md +432 -0
- package/docs/next/durable-observers/index.html +517 -0
- package/docs/next/flow-control/book.md +333 -0
- package/docs/next/flow-control/fold/index.html +265 -0
- package/docs/next/flow-control/index.html +548 -0
- package/docs/next/flow-control/window/index.html +228 -0
- package/docs/next/guide-plugins-hosts/book.md +607 -0
- package/docs/next/guide-plugins-hosts/index.html +652 -0
- package/docs/next/http-workers/book.md +378 -0
- package/docs/next/http-workers/index.html +515 -0
- package/docs/next/layer-1.md +311 -0
- package/docs/next/layer-2.md +216 -0
- package/docs/next/plugin-model/book.md +186 -0
- package/docs/next/plugin-model/index.html +310 -0
- package/docs/next/plugin-primer/book.md +273 -0
- package/docs/next/plugin-primer/index.html +430 -0
- package/docs/next/slice-1.md +236 -0
- package/docs/next/slice-10.md +285 -0
- package/docs/next/slice-11.md +93 -0
- package/docs/next/slice-2.md +174 -0
- package/docs/next/slice-3a.md +296 -0
- package/docs/next/slice-3b.md +197 -0
- package/docs/next/slice-4.md +146 -0
- package/docs/next/slice-4a.md +418 -0
- package/docs/next/slice-4b.md +208 -0
- package/docs/next/slice-5.md +281 -0
- package/docs/next/slice-6a.md +676 -0
- package/docs/next/slice-6b.md +332 -0
- package/docs/next/slice-7a.md +130 -0
- package/docs/next/slice-7b/book.md +608 -0
- package/docs/next/slice-7b/index.html +842 -0
- package/docs/next/slice-7b.md +255 -0
- package/docs/next/slice-7c.md +165 -0
- package/docs/next/slice-8.md +194 -0
- package/docs/next/slices.md +230 -0
- package/docs/next/store/book.md +665 -0
- package/docs/next/store/index.html +861 -0
- package/docs/next/store/walkthrough.md +193 -0
- package/docs/spec.md +9 -9
- package/docs/tutorial.md +26 -25
- package/package.json +22 -6
- package/src/delta/README.md +69 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Slice 1: one host, local objects (spec, for the owner's approval)
|
|
2
|
+
|
|
3
|
+
Status: **approved (owner, 2026-10-22)**, after Astra's and Fable's spec reviews. The single source for building slice 1,
|
|
4
|
+
together with the feature map in `slices.md` (entries assigned to slice 1) and `design.md`, read in full (the most
|
|
5
|
+
relevant sections: §3 host, §5-§7, §8, §9, §11, §12, §16, §18); rules come from the decision log
|
|
6
|
+
(`packages/chord/docs/next/decisions.md`); code style: the section "Chord v2 Code Style" of the root `AGENTS.md`. Builds on
|
|
7
|
+
L1 (`src/next/contracts`, `src/next/data`) and Chord's shared watch handle (`src/watch.ts`).
|
|
8
|
+
|
|
9
|
+
## What an app can do after this slice
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const Notes = local("notes.store", {
|
|
13
|
+
version: 1,
|
|
14
|
+
params: {},
|
|
15
|
+
members: {
|
|
16
|
+
notes: obj({ list: arr(Note) }),
|
|
17
|
+
titles: arr(str()),
|
|
18
|
+
count: int(),
|
|
19
|
+
add: method(obj({ text: str() })),
|
|
20
|
+
saveDraft: method(obj({ conversation: int(), body: str() })),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const Draft = local("notes.draft", {
|
|
24
|
+
version: 1,
|
|
25
|
+
params: { conversation: int() },
|
|
26
|
+
members: { summary: str() },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const NotesDoc = defineState("notes", { schema: obj({ list: arr(Note) }), version: 1, initial: () => ({ list: [] }) });
|
|
30
|
+
const Drafts = defineState("drafts", { schema: obj({ body: str() }), version: 1, initial: () => ({ body: "" }), keyed: true });
|
|
31
|
+
|
|
32
|
+
const facet = defineFacet<AppPrincipal>((host) => {
|
|
33
|
+
host.serve(Notes, (_, self) => { // empty params: lives as long as the host
|
|
34
|
+
const notes = self.state(NotesDoc);
|
|
35
|
+
const drafts = self.state(Drafts); // keyed: one document per conversation (P3 pattern)
|
|
36
|
+
const title = mapShared((note: Note) => note.text.slice(0, 20));
|
|
37
|
+
return {
|
|
38
|
+
notes,
|
|
39
|
+
titles: self.derive([notes], ([n]) => title(n.list)),
|
|
40
|
+
count: self.derive([notes], ([n]) => n.list.length),
|
|
41
|
+
add: async ({ text }, ctx) => {
|
|
42
|
+
await self.tx(async (tx) => { (await tx.draft(notes)).list.push({ text }); }, ctx);
|
|
43
|
+
return null;
|
|
44
|
+
},
|
|
45
|
+
saveDraft: async ({ conversation, body }, ctx) => {
|
|
46
|
+
await self.tx(async (tx) => { (await tx.draft(drafts, String(conversation))).body = body; }, ctx);
|
|
47
|
+
return null;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
host.serve(Draft, ({ conversation }, self) => { // params: lives while referenced
|
|
52
|
+
const store = self.get(Notes, {}); // declared here, live before this object is
|
|
53
|
+
return { summary: self.derive([store.count], ([c]) => `conversation ${conversation}, ${c} notes`) };
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const host = createHost({ onError: (error) => log(error) }); // no address: local types only
|
|
58
|
+
await host.load([{ name: "@app/notes", facet }], ctx);
|
|
59
|
+
const notes = await host.get(Notes, {}, ctx); // live and hydrated
|
|
60
|
+
await notes.add({ text: "milk" }, ctx);
|
|
61
|
+
notes.count.value; // 1
|
|
62
|
+
const w = await notes.count.watch(ctx);
|
|
63
|
+
w.start(async (value) => console.log(value));
|
|
64
|
+
notes.release(); // ends w; the object lives on (empty params)
|
|
65
|
+
await host.close(ctx);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 1. Host and facets
|
|
69
|
+
|
|
70
|
+
- `createHost({ telemetry?, onError? })`: a host without an address; it serves only `local` types in this slice.
|
|
71
|
+
`telemetry` is the adapter (NOOP by default); `onError` receives background errors (a throwing watch listener).
|
|
72
|
+
- `defineFacet<P>(setup)` returns a facet typed for the app's principal type `P` (base code binds it once; nothing is
|
|
73
|
+
checked at runtime). `setup(host, self)` runs synchronously inside `host.load` and only declares: `host.serve(Type,
|
|
74
|
+
factory)` and facet documents through `self.state(Def)` (C16, owner: built now). `host.serve` outside a setup
|
|
75
|
+
throws.
|
|
76
|
+
- **Facet documents (design §9, §16):** the facet is a pure object with the host's lifetime; its documents live as long
|
|
77
|
+
as the host. Objects the facet serves may read the facet's handles and draft them in their own `self.tx`, together
|
|
78
|
+
with their own documents, atomically (lock order: the object's own line, then the facet's line). The facet never
|
|
79
|
+
drafts a served object's documents; objects of other facets cannot use them.
|
|
80
|
+
- `host.load(facets, context)`: `facets` is `[{ name, facet }]` with npm-style plugin names; duplicate names or a type
|
|
81
|
+
served twice are refused. After every setup ran, every served `local` type with empty params is activated (Q-A, the
|
|
82
|
+
start barrier); `load` resolves when all are live. If one fails, `load` ends everything it activated and rejects;
|
|
83
|
+
the host is then empty. A `host.get` made before `load` resolves waits for it.
|
|
84
|
+
- `host.close(context)`: rejects new calls, ends every object (in reverse activation order) and resolves when all have
|
|
85
|
+
ended.
|
|
86
|
+
|
|
87
|
+
## 2. Objects
|
|
88
|
+
|
|
89
|
+
- **Identity:** one object per `(type, params key)` (L1's `checkParams` and `identityKey`; local has no principal).
|
|
90
|
+
- **Lifetime (Q-A):** a local type with empty params is created at `load` and lives until `close`; a type with params
|
|
91
|
+
is created by the first `get` and ends when its last reference is released.
|
|
92
|
+
- **Factory:** `(params, self) => implementation`, synchronous and declarative, run once per object. The
|
|
93
|
+
implementation has exactly the contract's members: a state member is a JSON `Watchable` of its value (a document
|
|
94
|
+
handle, a derive, a member of a reference), a method is `(args, ctx) => Promise<result>`. L1's `Implementation<C>`
|
|
95
|
+
types state members as `Watchable<Static<schema>>` (owner, 2026-10-22).
|
|
96
|
+
- **Activation (design §6):** run the factory; acquire the references it declared with `self.get` (activating their
|
|
97
|
+
objects if needed) and wait until they are live; check the members (exactly the contract's, state members JSON
|
|
98
|
+
watchables, Q14); compute every derive's first value; then the object is live and calls proceed. A throw anywhere
|
|
99
|
+
in activation (factory, a dependency's activation, a member check, a first derive compute, `initial()` failing its
|
|
100
|
+
schema) rejects the `get` (or `load`) with that error, releases everything acquired and leaves no object. Failure
|
|
101
|
+
states, backoff and retries are slice 2.
|
|
102
|
+
- **Ending:** new calls on the object fail `released`; the context of each of its running calls is aborted; from then
|
|
103
|
+
on its `self.tx` throws `released` (a still-running method can no longer change anything); its derives are detached
|
|
104
|
+
from their sources (no source keeps a reference to an ended object's derive); its references are released; its
|
|
105
|
+
documents are dropped; every watch on its members ends `ended`. Ending does not wait for running calls (the drain
|
|
106
|
+
arrives with natives in slice 2, design §3).
|
|
107
|
+
|
|
108
|
+
## 3. References
|
|
109
|
+
|
|
110
|
+
- `host.get(Type, params, context)` returns a reference once its object is live; `self.get(Type, params)` declares one
|
|
111
|
+
in a factory: it is live by the time the object goes live, and released when the object ends.
|
|
112
|
+
- A reference is a plain object built from the contract's member list (no proxy): `ref.<state>` is a `Watchable`
|
|
113
|
+
(`value`, `watch(ctx)`), `ref.<method>(args, ctx)` calls the method, `ref.release()` releases it (idempotent). After
|
|
114
|
+
release, every use throws `released`.
|
|
115
|
+
- **Calls:** arguments and results pass by reference; arguments are checked at the provider with L1's `checkArgs`
|
|
116
|
+
against the current version (`bindProvider(contract, {})` at serve): unknown method `invalid/member`, bad arguments
|
|
117
|
+
`invalid/args`; results are not checked; no-argument methods take `null`.
|
|
118
|
+
- **Per-reference watches** (owner): watches obtained through a reference end (`ended`) when it is released, even
|
|
119
|
+
while other references keep the object alive.
|
|
120
|
+
- **Contract match** (owner): at `get`, the caller's contract must have the provider's version and fingerprint (the
|
|
121
|
+
same declaration, or an identical one), otherwise `contract_mismatch`. Older versions through `accepts` come with
|
|
122
|
+
slice 7.
|
|
123
|
+
- **Reserved member names** (owner): `then`, `release`, `state` and `retry` are refused as member names at
|
|
124
|
+
declaration (an L1 check in `remote`, `local` and `evolve`), because a reference has them (`release` now, `state` and
|
|
125
|
+
`retry` in slice 2) and `then` would make an awaited reference thenable.
|
|
126
|
+
- **Cycles (Q5, one host; owner):** at `self.get` during an activation, Chord follows the chain of objects waiting for
|
|
127
|
+
activation; reaching the object being activated fails that activation with `invalid/cycle`, naming the cycle
|
|
128
|
+
(`a → b → a`); nothing is kept.
|
|
129
|
+
- `ctx` in this slice is a plain `Context` (cancellation and telemetry); `ctx.get` and `ctx.principal` come with
|
|
130
|
+
slice 3.
|
|
131
|
+
|
|
132
|
+
## 4. Documents in memory (§9, P2)
|
|
133
|
+
|
|
134
|
+
- `defineState(name, { schema, version, initial, keyed? })`: the root is a JSON object. `checkpointWhen` is required
|
|
135
|
+
only for persisted documents (slice 6; owner, 2026-10-22). `migrate` comes with slices 6 and 7.
|
|
136
|
+
- `self.state(Def)` returns a handle bound to this object; every document is in memory, instance state of the object
|
|
137
|
+
(P2), gone when it ends.
|
|
138
|
+
- **Singleton handle:** `handle.value` is the current value, `initial()` until written or after a retire (Q11); the
|
|
139
|
+
handle is a `Watchable`.
|
|
140
|
+
- **Keyed handle:** `await handle.get(key)` gives the value or `undefined`; `handle.keys()` is an `AsyncIterable` of
|
|
141
|
+
the present keys (the shape persistence keeps); keys are strings. A keyed handle is not a `Watchable` (a live
|
|
142
|
+
collection is a singleton holding a list or map).
|
|
143
|
+
- `initial()` is checked against the schema once, the first time a definition's `initial()` is used (R8).
|
|
144
|
+
|
|
145
|
+
## 5. Transactions (§9, R12, R13)
|
|
146
|
+
|
|
147
|
+
- `self.tx(fn, ctx)`: an object's transactions run one at a time (its line). `fn` gets `tx`: `await tx.draft(handle,
|
|
148
|
+
key?)` returns the document's draft (Delta's `track`, `beginChange`; the same document gives the same draft within
|
|
149
|
+
one transaction); `tx.retire(handle, key?)` removes a document (a no-op when absent). `self.tx` returns what `fn`
|
|
150
|
+
returns.
|
|
151
|
+
- **Commit:** when `fn` resolves, if `ctx` is aborted the transaction is discarded; otherwise every draft is prepared
|
|
152
|
+
(Delta's `prepare`: ops recorded, only placed values checked), unchanged drafts commit nothing, and all changed
|
|
153
|
+
documents are adopted (pointer swap) in **one batch** (section 6). A throw discards everything.
|
|
154
|
+
- Drafts are revoked when the transaction settles (touching one throws, R12); a transaction returns plain values and
|
|
155
|
+
data is read from the handle after the commit.
|
|
156
|
+
- A transaction drafts only its own object's documents and its facet's (any other handle throws). Inside `fn`: no
|
|
157
|
+
second `self.tx`, no calls to other objects (Durable's rule; documented, not detected).
|
|
158
|
+
- **Shorthands (C14, owner: built now):** `handle.change(fn, ctx)` (keyed: `handle.change(key, fn, ctx)`) runs one
|
|
159
|
+
transaction that loads the document and calls the synchronous `fn(draft)`; `handle.retire(ctx)` (keyed:
|
|
160
|
+
`handle.retire(key, ctx)`) retires it. Both are thin wrappers over `self.tx`.
|
|
161
|
+
|
|
162
|
+
## 6. Derives and the batch (W1, §11, §12)
|
|
163
|
+
|
|
164
|
+
- `self.derive(sources, compute)`: sources are JSON `Watchable`s (this object's handles and derives, members of
|
|
165
|
+
its `self.get` references); only the declared sources are tracked. `compute` is a synchronous pure function of their
|
|
166
|
+
values; its result is JSON.
|
|
167
|
+
- **Pull model.** A commit only marks: every derive that depends on a changed document, directly or through other
|
|
168
|
+
derives, including derives of other objects on the host, is flagged dirty (O(dependents), flags only). A derive's
|
|
169
|
+
`value` recomputes when it is dirty, after reading its sources' values (which recompute first if they are dirty);
|
|
170
|
+
so each derive computes at most once per batch and always after its sources, diamonds included.
|
|
171
|
+
- **The batch** (one per host): a commit's adoptions happen inside it; after the last adoption, still synchronously,
|
|
172
|
+
every watched value that changed is read (watched derives recompute through the pull), its ops computed
|
|
173
|
+
(`diffRevisions(old, new)` for a derive, the commit's ops for a document), and its watches get one frame each. A
|
|
174
|
+
derive is recomputed in the batch only if something watched depends on it; otherwise it stays dirty until read.
|
|
175
|
+
Reading `.value` once does not make it watched.
|
|
176
|
+
- A derive that throws fails only itself: its `value` throws the error and its watches end `failed`; a derive over it
|
|
177
|
+
throws the same error when read. A derive that fails during activation fails the activation (section 2). A failed
|
|
178
|
+
derive stays failed until the object is rebuilt.
|
|
179
|
+
- **Reentrancy:** inside the batch (adoptions, derive computes, including lazy computes outside a commit), `self.tx`,
|
|
180
|
+
`watch`, `release`, `get`, `load` and `close` throw `invalid/reentrant`. One flag per host, cleared in a `finally`.
|
|
181
|
+
- `mapShared(f)`: maps an array calling `f` only for object elements not seen before (by identity), returning the
|
|
182
|
+
previous result for unchanged ones; primitive elements are mapped every time; the cache is a `WeakMap`.
|
|
183
|
+
- `constant(value)`: a JSON `Watchable` that never changes (start data, §18); usable as a state member and a source.
|
|
184
|
+
- State is JSON only (L2 decision): no bytes in documents or derives.
|
|
185
|
+
|
|
186
|
+
## 7. Watching
|
|
187
|
+
|
|
188
|
+
`watchable.watch(ctx)` returns Chord's shared `Watch` (`src/watch.ts`): the value at acquisition, then one frame per
|
|
189
|
+
batch that changed it, `(value, ops, ctx)`, in order, asynchronously, with its overflow, `stop` and end semantics.
|
|
190
|
+
Each frame is consistent with its commit; two different watches are not ordered relative to each other. A retired
|
|
191
|
+
singleton gives the frame `[["r", initial]]` (it changes back to `initial()`; a singleton always has a value). `ctx`'s
|
|
192
|
+
abort cancels the watch. A listener that throws ends its watch `listener_error` and is reported to `onError` (taken
|
|
193
|
+
from the carry-over).
|
|
194
|
+
|
|
195
|
+
## Cost per hop
|
|
196
|
+
|
|
197
|
+
| Hop | Work |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| a call | L1's three lookups and one compiled check; arguments by reference |
|
|
200
|
+
| a commit | Delta prepare per changed document, pointer swaps, dirty flags per dependent, then per watched changed value one read (derives recompute once) and one frame per watch, by reference |
|
|
201
|
+
| an unwatched derive | nothing until read |
|
|
202
|
+
|
|
203
|
+
Counters (telemetry, where the work happens): L1's, plus `chord.derive.computes` and `chord.derive.diffs`.
|
|
204
|
+
|
|
205
|
+
## Carry-over taken by this slice
|
|
206
|
+
|
|
207
|
+
From `carry-over.md` (L4 section): watch listener errors reported to `onError`; native watchables refused as state
|
|
208
|
+
(by the member check: only JSON watchables exist in this slice); `defineFacet` typed by `P`. Left for
|
|
209
|
+
later: plugin error shape, old-version calls (7), `broken()` (2), cuts C5, C6, C7, C13 (2).
|
|
210
|
+
|
|
211
|
+
## Tests
|
|
212
|
+
|
|
213
|
+
- Host: load runs setups, activates empty-params types, rejects duplicates and double serves; a failing activation
|
|
214
|
+
makes `load` end what it activated; `get` during load waits; `serve` outside setup throws; close ends everything.
|
|
215
|
+
- Objects: one object per params key; params type created on first get and ended on last release (documents gone,
|
|
216
|
+
watches end `ended`, its derives detached from other objects' members); empty-params type lives across releases;
|
|
217
|
+
activation failures (factory, dependency, member check, first derive, `initial()`) reject the get and leave nothing;
|
|
218
|
+
a method running while its object ends cannot commit (`self.tx` throws `released`) and sees its `ctx` aborted.
|
|
219
|
+
- References: hydrated on get; members watchable; calls checked; released reference throws; `self.get` dependency
|
|
220
|
+
kept alive and released on end; per-reference watches; contract mismatch; reserved member names refused at
|
|
221
|
+
declaration; a two-object and a three-object cycle fail with `invalid/cycle` and leave nothing.
|
|
222
|
+
- Documents: singleton initial, retire back to initial; keyed get, keys, retire; `initial()` checked once.
|
|
223
|
+
- Transactions: serialized per object; same draft twice; unchanged commits nothing; throw and abort discard; drafts
|
|
224
|
+
revoked after settle; another object's handle refused; a served object's transaction over its own and its facet's
|
|
225
|
+
documents commits both in one batch; shorthands.
|
|
226
|
+
- Batch and derives: two documents in one transaction reach a derive over both in one compute and a watch in one
|
|
227
|
+
frame; a derive over another object's member updates in that object's commit's batch; diamond computes once;
|
|
228
|
+
unwatched derive stays lazy and a one-off read does not make it eager; a throwing derive fails only itself and its
|
|
229
|
+
dependents; reentrancy throws in commits and lazy computes and the flag clears after a throw; `mapShared` identity.
|
|
230
|
+
- Watching: frames in order, retire frame, cancellation, listener error reported.
|
|
231
|
+
- Cost guards per the table. GC: an ended object with its documents, derives and watches is collected; a released
|
|
232
|
+
reference and a stopped watch hold nothing; a detached derive is not held by its former sources.
|
|
233
|
+
|
|
234
|
+
## Size
|
|
235
|
+
|
|
236
|
+
About 1,100 source lines.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Slice 10: HTTP and workers (spec)
|
|
2
|
+
|
|
3
|
+
Status: **built** (2026-10-31; build reviews by Fable, Opus, Astra and Sol, security audits by Fable, Opus and Astra, four fix rounds with rechecks; the shape after the fix rounds is in §2, §3 and design §23). Approved by the owner (2026-10-30: "just build it"; D1-D12, D10b, round 1's R1-R6 and round 2's Q1-Q6 in the
|
|
4
|
+
decision "Slice 10's points"; spec reviews round 1 by Fable and Opus, `/tmp/s10-spec-review-{fable,opus}.md`, round 2
|
|
5
|
+
by Fable, Opus, Astra and Sol, `/tmp/s10-spec-review2-*.md`, folded in).
|
|
6
|
+
Slice 10 builds two Node components of the app kit (decision "Slice 7b's open points" (6)): `chord/next/http` (the
|
|
7
|
+
server's HTTP and WebSocket entry, routes contributed by plugins through a registry, responses as descriptions) and
|
|
8
|
+
`chord/next/workers` (a worker process per host address, started on demand, stopped when idle, its plugins shipped from
|
|
9
|
+
the server). Both live in `packages/chord/src/next-node/` (Node-only); the kernel stays portable. The web page host and
|
|
10
|
+
the TUI are slice 11, telemetry slice 9.
|
|
11
|
+
|
|
12
|
+
Read in full before writing code: this spec; `decisions.md`; `design.md` (§3 hosts, routes, `host.activity`, §17,
|
|
13
|
+
§18, §21 "Loading plugins and the reload loop", §23 in full); `slices.md`; `slice-7c.md` and
|
|
14
|
+
`src/next-node/plugins/README.md`; the 7b brief `slice-7b/book.md` (chapters "Assets and Files", "HTTP", "A Mini p2
|
|
15
|
+
from Chord"); p2's `src/hosts/server.ts`, `src/runtime/server.ts`, `src/hosts/worker.ts`, `src/hosts/socket-limits.ts`,
|
|
16
|
+
`src/hosts/principals.ts`, `plugins/files/server.ts`, `plugins/terminals/server.ts`, `plugins/radius/server.ts`; Chord
|
|
17
|
+
v1's `src/manager.ts` (`processLock`, `forkDriver`, `createHostManager`, `childControl`).
|
|
18
|
+
|
|
19
|
+
## 1. What p2 has, and what moves into Chord
|
|
20
|
+
|
|
21
|
+
p2's server host (`src/hosts/server.ts`, 742 lines; `src/runtime/server.ts`, 720) does, besides p2's own policy:
|
|
22
|
+
|
|
23
|
+
| p2 today | Moves to |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| a Node `http` server on loopback, requests parked until `load` resolved, upgrades captured before listening | `http` |
|
|
26
|
+
| `Routes` (`"METHOD /path"`, `/prefix/*`), handlers writing Node's `ServerResponse` themselves (ranges, download headers, `HEAD` by hand in `files`) | `http`: routes returning descriptions |
|
|
27
|
+
| `Upgrades` (a WebSocket by path for terminals and voice), `/chord` (`acceptWebSockets` with per-socket limits) | `http` |
|
|
28
|
+
| `Authenticators`, `RequestResolvers` (cookies, devices, the Radius relay), Fetch Metadata, CSP and the loopback cookie | the app (p3's base code), through `http`'s `authenticate` and `headers` (D3) |
|
|
29
|
+
| loopback and allowed hosts, the `Origin` check on upgrades | `http` (R4), allowed hosts contributable by plugins |
|
|
30
|
+
| `SessionHosts`: a worker per session, `createHostManager` + `forkDriver`, a kernel lock per session, idle stop from reported counts, crash notices, sticky failed starts, ready timeout | `workers` (failures stay until Retry or a rollout, R2) |
|
|
31
|
+
| relocation and pause of a worker, the catalog, `WorkerInitValues` | the app (built on `workers`' `stop` and start data) |
|
|
32
|
+
| `hosts/worker.ts`: the child's control channel, lock, start data, connect, load, report counts, shutdown | `workers`' child side |
|
|
33
|
+
| the dev reload across server and workers, worker plugin lists | `plugins` (7c) plus `workers`' rollout step (§4) |
|
|
34
|
+
|
|
35
|
+
Every option that is not bootstrap is changeable by plugins (decision "The app kit is malleable by plugins"): either a
|
|
36
|
+
contribution to `Http` or a function the component calls per use, which the app backs with its own registries;
|
|
37
|
+
nothing is copied out of a contribution or an option at startup. Bootstrap (changed by a restart): the listen address
|
|
38
|
+
and port, the links path, the worker entry with its `execArgv` and `env`, the host kind workers serve.
|
|
39
|
+
|
|
40
|
+
## 2. `chord/next/http`
|
|
41
|
+
|
|
42
|
+
As built after the fix rounds (decisions "The HTTP core is a fetch handler", "The HTTP server is a native object",
|
|
43
|
+
"Settings are arguments, extensions are registries", "The HTTP component's security baseline", "allowed hosts only
|
|
44
|
+
through contributions", "A CSP nonce per response", "three small points", "a route's overhead accepted"); design §23
|
|
45
|
+
states every rule, `src/next-node/http/README.md` the API.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
const http = createHttp(server, {
|
|
49
|
+
listen: { host: "127.0.0.1", port: 8788 }, // settings: arguments, a restart to change
|
|
50
|
+
links: { path: "/chord", options: (request, principal) => ({ limits: linkLimits(principal), maxPayload: 64 * MiB }) },
|
|
51
|
+
closeTimeout: 30_000,
|
|
52
|
+
onError: (error) => log(error), // `HttpError`, below
|
|
53
|
+
}, ctx);
|
|
54
|
+
const app = defineFacet((_host, self) => {
|
|
55
|
+
self.contribute(Authenticators, (request, ctx) => principalFromCookie(readCookie(request, "session", secret)));
|
|
56
|
+
self.contribute(Http, { hosts: ["notes.example.com"], relays: [radiusRelay] });
|
|
57
|
+
});
|
|
58
|
+
await plugins.load(server, { kind: "server", core: [{ name: "chord.http", facet: http.facet }, { name: "@app/core", facet: app }], modules }, ctx);
|
|
59
|
+
…
|
|
60
|
+
await http.close(ctx); // first: no new requests, sockets closed, running requests waited for up to `closeTimeout`
|
|
61
|
+
await workers.close(ctx);
|
|
62
|
+
await server.close(ctx);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- **The core** is `http.fetch(request, { localAddress?, localPort?, remoteAddress? })`, a Web `Request` to a Web
|
|
66
|
+
`Response`; `http.upgrade` is Node's upgrade adapter; `nodeRequest(message, res)` converts a Node request with the
|
|
67
|
+
listener's checks (`{ kind: "request", request, connection } | { kind: "refused", status }`, never a throw) for apps
|
|
68
|
+
mounting on their own Node server. Chord's own listener (with `listen`) is a host-lifetime object of `chord.http`
|
|
69
|
+
whose native is a Node server, on a fast path that skips the Web `Request` unless a route or authenticator reads
|
|
70
|
+
it.
|
|
71
|
+
- **Registries:** `Http` (`"chord.http"`, version 1: `routes`, `wraps`, `sockets`, `maxPayload`, `hosts`, `framers`,
|
|
72
|
+
`relays`, `csp`; a value or a writable) and `Authenticators` (in contributor order, the first principal wins).
|
|
73
|
+
Requests before the publication wait for it (503 past `readyTimeout`).
|
|
74
|
+
- **Admission, foreign requests, origins, default headers, cookies, downloads:** the security baseline (design §23).
|
|
75
|
+
Refusals before any plugin code: a non-loopback socket without an allowed host, forwarded headers without a
|
|
76
|
+
verified relay, duplicate or missing `Host`, userinfo in `Host`, absolute-form targets (403), unparsable targets
|
|
77
|
+
(400), `TRACE`/`TRACK`/`CONNECT` (501; a route for them refused at compose).
|
|
78
|
+
- **A request's scope, `HttpRequest`, patterns, wraps, responses, `fileResponse`, sockets, links, revocation,
|
|
79
|
+
`close`:** design §23. The spec's earlier text (an `httpServer` that listens first, `allowedHosts`, one
|
|
80
|
+
`authenticate` and a `headers` option, 64 KiB reads) is superseded by those decisions.
|
|
81
|
+
- **Errors** (`onError`, `HttpError`): `route_failed { pattern, plugin, error }`, `wrap_failed { name, plugin, error }`,
|
|
82
|
+
`socket_failed { pattern, plugin, error }`, `response_failed { pattern, error }`, `hook_failed { hook, plugin?,
|
|
83
|
+
error }` (`authenticate`, `relay`, `links.options`).
|
|
84
|
+
- `ws` is an optional peer dependency (D7), imported only here.
|
|
85
|
+
|
|
86
|
+
## 3. `chord/next/workers`
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// server.ts
|
|
90
|
+
const workers = createWorkers(server, Session, {
|
|
91
|
+
kind: "worker", // the facet kind it ships
|
|
92
|
+
entry: "./worker.ts", execArgv: ["--import", resolver], env: { … }, // bootstrap
|
|
93
|
+
authorize: (principal, address) => app.mayReach(principal, address), // the route's `authorize` (synchronous)
|
|
94
|
+
principal: (address) => ({ kind: "worker", session: address.params.id }),
|
|
95
|
+
start: (address, ctx) => app.startData(address, ctx), // JSON, before each start
|
|
96
|
+
lock: (address) => `app:${realpath(dataDir)}:worker:${address.key}`, // one live worker per address
|
|
97
|
+
idleMs: () => app.idleMs(),
|
|
98
|
+
plugins, // the plugins component
|
|
99
|
+
onOutput: (address, stream, chunk) => logs.append(address.key, stream, chunk),
|
|
100
|
+
onError: (error) => log(error), // `WorkersError`, below
|
|
101
|
+
}, ctx);
|
|
102
|
+
workers.state // JSON watchable (R5)
|
|
103
|
+
await workers.stop(Session({ id }), ctx); // archive: suspended while held (Q3), then stopped
|
|
104
|
+
await workers.retry(Session({ id }), ctx); // the Retry button: a failed or suspended address resolves again
|
|
105
|
+
await workers.close(ctx); // no new starts, every child stopped
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// worker.ts
|
|
110
|
+
await workerMain(async ({ address, start, parent, plugins, onClose }, ctx) => {
|
|
111
|
+
const store = await openStore(sqliteBackend(await openNodeSqliteDatabase(join(start.dir, "session.sqlite"))), ctx);
|
|
112
|
+
onClose((ctx) => store.close(ctx)); // closers run in reverse order
|
|
113
|
+
const worker = createHost<P>({ address, principal: { kind: "worker", session: start.id }, store, onError });
|
|
114
|
+
onClose((ctx) => worker.close(ctx));
|
|
115
|
+
worker.connect(parent, { principal: { kind: "server" }, trusted: true }, ctx); // `started` goes out now: connect
|
|
116
|
+
// before anything slow (a store opens in ms)
|
|
117
|
+
await plugins.load(worker, { kind: "worker", core: [{ name: "@app/core", facet: coreWorker(start) }] }, ctx);
|
|
118
|
+
return { busy: harness.activeTasks }; // extra keep-alive counts
|
|
119
|
+
}, {
|
|
120
|
+
hostPackages: { "@earendil-works/chord/next": chordNext, "@app/core": appCore }, // the child's own instances
|
|
121
|
+
redact: (error) => ({ message: redactSecrets(String(error)), ...codeOf(error) }), // what crosses as `fatal`
|
|
122
|
+
onError: (error) => log(error),
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- **The route:** `createWorkers` calls `host.route(Kind, { authorize, resolve }, ctx)` itself and keeps the
|
|
127
|
+
`RouteHandle` (`retry`, `suspend`: D10b, R1). `authorize` is synchronous (the kernel's): a plugin backing it keeps an
|
|
128
|
+
index.
|
|
129
|
+
- **Per address, events and operations:** a child's events (its exit, its link's close, `started`, `ready`, `fatal`)
|
|
130
|
+
are observed as they happen and recorded at once on that child (a late event of an old child never changes a new
|
|
131
|
+
one); the operations (start, stop) run in one queue per address: a start waits for the previous child's exit, a
|
|
132
|
+
stop during a start waits for it then stops, and a start queued behind an exit checks the address's state after it
|
|
133
|
+
(a crash recorded meanwhile makes it reject with that failure). Stops record their intent before `shutdown`, so the
|
|
134
|
+
exit they cause is no crash.
|
|
135
|
+
- **A start** (`resolve(address)`): calls `start(address, ctx)` (abortable; a throw rejects the start), checks that the
|
|
136
|
+
component is not closing, forks `entry` (`serialization: "advanced"`, a detached process group, stdout and stderr
|
|
137
|
+
piped: the stderr tail kept, mirrored to the server's when there is no `onOutput`), makes `ipcLink(child)` at once
|
|
138
|
+
(so the child's `hello` and early holds are queued, never lost), and sends the start message `{ address, start,
|
|
139
|
+
scripts, lock }` (`scripts`: the worker kind's complete current set, read when the message is sent). The child takes
|
|
140
|
+
the lock (when an old worker still holds it, it waits by connecting to the lock's socket and trying again when that
|
|
141
|
+
connection closes: no polling), and sends `started` when the app's host sets its listener on `parent` (`parent` is
|
|
142
|
+
a thin wrapper of the IPC link, so `connect` is the signal: the app connects before anything slow). `resolve` then
|
|
143
|
+
returns `{ link, principal: principal(address), trusted: true }`, and the handshake and the worker's server traffic
|
|
144
|
+
run during its `load`. The start timer (60 s from the fork) covers fork, lock and connect only (Q4): past it the tree is
|
|
145
|
+
killed and the start fails. `ready` (after the app's function returned, carrying the idle state and its load report) sets `state` to
|
|
146
|
+
`running` and lets idle stops begin; a `load` that never ends shows `starting` until `stop`.
|
|
147
|
+
- **Workers never restart by themselves** (R2): a crash (an exit nobody asked for), a `fatal`, a link lost while its
|
|
148
|
+
child lives, or a failed start (`start` threw, the start timer, a lock not taken in time) leaves the address `failed`
|
|
149
|
+
with its error (`worker_failed` reported once). A link lost while its child lives (the heartbeat after a blocked
|
|
150
|
+
event loop, a protocol error) starts the stop: the grace (5 s), then the tree is killed; its exit is the crash ("link
|
|
151
|
+
lost"). `resolve` of a failed address rejects at once with that error; its holders and new holds see `failed {
|
|
152
|
+
waiting }` with it as cause (Q1) and stay on the address's path, nothing forks. `workers.retry(address)`
|
|
153
|
+
(`RouteHandle.retry`) or a rollout reaching workers clears it (Q2, §4). An idle stop is no failure: the next hold
|
|
154
|
+
starts the worker on demand.
|
|
155
|
+
- **Keep-alive** (D9, R3, Q5): the child's idle state is all of `host.activity`'s `references`, `jobs` and `lingering`
|
|
156
|
+
and the app's `busy` (a JSON watchable of numbers) at zero, not `calls` (a remote call runs on a hold; local calls
|
|
157
|
+
come from routines, jobs and tasks counted elsewhere). `ready { idle }` carries the first state, then `{ idle }` on
|
|
158
|
+
each flip; a rollout applying in the child counts as busy. The server stops a worker idle since `idleMs()` (5 min by
|
|
159
|
+
default; checked every second), never before `ready`. A stop (idle, `stop`, `close`): `shutdown`; the child aborts its
|
|
160
|
+
`ctx`, runs the closers registered with `onClose` in reverse order and exits, forcing its own exit after 3 s; the
|
|
161
|
+
server kills the tree after 5 s.
|
|
162
|
+
- **`state`** (R5, decision "worker errors in public"): per address key `{ status: "starting" | "running" |
|
|
163
|
+
"stopping", pid, startedAt }` or `{ status: "failed", error: { message, code?, reason? }, at }` with a public error
|
|
164
|
+
(a fixed message, or the child's redacted `fatal`); no entry when stopped or never started. The full error and the
|
|
165
|
+
stderr tail go only to `onError` (`worker_failed`); every app hook of a start (`start`, `principal`, `lock`, the
|
|
166
|
+
fork) is inside that boundary.
|
|
167
|
+
- **`stop(address, ctx)`** (R1, Q3): `RouteHandle.suspend(address)`, then the stop. The suspension lasts while the
|
|
168
|
+
address has holders (they see `failed { waiting }`, "suspended"); with the last holder it goes, and a later hold
|
|
169
|
+
starts the worker on demand; `retry(address)` lifts it at once. Whether an archived session may be opened is the
|
|
170
|
+
app's (`authorize`, its views).
|
|
171
|
+
- **`close(ctx)`:** cancels active and queued starts first (a `start(address)` waiting on the app's barrier is
|
|
172
|
+
aborted; nothing forks after it), then stops every child (grace, then kill), clears the timers; new starts reject
|
|
173
|
+
`released/closed`; the caller's cancellation ends only its own wait. `createWorkers`'s `ctx` aborting (the host
|
|
174
|
+
closing aborts the route's context) closes it too, and from then on every exit is a stop, never a crash (no
|
|
175
|
+
`worker_failed` at shutdown).
|
|
176
|
+
- **The child** (`workerMain(fn, { hostPackages, redact, onError })`): reads the start message, takes the lock, makes
|
|
177
|
+
`ipcLink(process)` and its wrapper `parent`, creates its plugin runtime with `hostPackages` (the child's own module
|
|
178
|
+
instances; functions cannot cross IPC), calls `fn({ address, start, parent, plugins, onClose }, ctx)`. `shutdown`,
|
|
179
|
+
the parent's disconnect, `SIGTERM` and `SIGINT` alike abort `ctx` and run the closers registered so far, also while
|
|
180
|
+
`fn` still runs (its startup is cancelled through `ctx`). An uncaught error, or `fn` throwing, sends `fatal` with
|
|
181
|
+
`redact(error)` (never the original message) and exits; after `started` that is a crash like any other.
|
|
182
|
+
- **Control messages** (JSON beside the link's `Uint8Array` frames; the IPC link skips them): parent to child `start`,
|
|
183
|
+
`shutdown`, `reload { scripts }`; child to parent `started`, `ready { idle }`, `idle { idle }`, `fatal { error }`,
|
|
184
|
+
`reloaded { report } | { error }`. One `reload` is outstanding per child at a time: a reload whose caller aborted
|
|
185
|
+
keeps the child's slot until its `reloaded` or the child's exit, and the next waits for it. Errors cross as `{
|
|
186
|
+
message, code?, reason? }`.
|
|
187
|
+
- **Errors** (`onError`, `WorkersError`, which includes `BatchError` for a `state` watch listener that throws): `worker_failed { address, error, stderrTail? }` (private),
|
|
188
|
+
`authorize_failed { address, error }` (a throwing `authorize`: the hold is refused `forbidden`), `worker_exited {
|
|
189
|
+
address, code, signal }` (only for an exit nobody asked for: not after a stop or a start the component failed).
|
|
190
|
+
|
|
191
|
+
### Plugins in workers (D11)
|
|
192
|
+
|
|
193
|
+
Workers never build. The plugins component (7c) gets `scripts(kind, ctx)`: the complete current set of one kind (every
|
|
194
|
+
discovered plugin's facet of it, built on first use, then updated by every rollout that ships that kind), each with its
|
|
195
|
+
plugin record (`name`, `dir`, `chord.external`). The component's per-host loading (its loaded map, `#prepare`, `adopt`,
|
|
196
|
+
the queue per host) moves into a file without esbuild or the watcher, `createPluginRuntime()`
|
|
197
|
+
with `load(host, { kind, core, scripts, modules }, ctx)` (`modules`: the host packages' module objects of this process) and `reload(host, scripts, ctx)` (a complete set: removed = loaded −
|
|
198
|
+
shipped; same digests unchanged); `createPlugins` uses it, and the child's `plugins` is a `WorkerPlugins` over one
|
|
199
|
+
(`load(host, { kind, core })` loads the start message's scripts; `reload` messages wait until `fn` returned); `import`
|
|
200
|
+
of `chord/next/plugins/runtime` loads no esbuild. Each kind is defined once in `createPlugins({ kinds: { <kind>: {
|
|
201
|
+
platform, hostPackages } } })` (decision "Slice 11's points", Kinds).
|
|
202
|
+
|
|
203
|
+
## 4. The rollout across server and workers
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
const built = await plugins.build(plugins.state.value.dirty, ctx);
|
|
207
|
+
const result = await plugins.rollout([plugins.step("server", server), workers.step("workers")], built, ctx);
|
|
208
|
+
// result.steps: [{ name: "server", hosts: [{ host: "server", report }] },
|
|
209
|
+
// { name: "workers", hosts: [{ host: "session|S1", report }, { host: "session|S2", stopped: true },
|
|
210
|
+
// { host: "session|S3", error }] }]
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`RolloutStep.reload(built, ctx)` returns `{ hosts: readonly ({ host; report } | { host; stopped: true } | { host; error
|
|
214
|
+
})[] }` (D12; 7c's in-process step returns a list of one, its `host` the name given to `step`). `workers.step(name)`:
|
|
215
|
+
|
|
216
|
+
1. replaces the workers' current set (`plugins.scripts(kind)` updated from `built`);
|
|
217
|
+
2. clears the failures recorded before this rollout and calls `RouteHandle.retry` for those addresses that have holders
|
|
218
|
+
(Q2): they start with the new set and are listed; an unheld one starts at its next hold;
|
|
219
|
+
3. ships the complete set to every running or starting worker whose start message or last reload carried an older one
|
|
220
|
+
(a child queues a reload that arrives before its `ready` until its load is done); workers reload in parallel.
|
|
221
|
+
|
|
222
|
+
A worker's entry settles on its `reloaded` or on its exit or link loss, whichever comes first. A worker stopped cleanly
|
|
223
|
+
during the step (idle, `stop`) is listed `stopped` and dirties nothing (its next start gets the current set); one that
|
|
224
|
+
crashed or failed to apply is listed with its error and does not stop the rollout, and every plugin of its kind in the
|
|
225
|
+
build counts as failed there (stays dirty); a worker failing during this rollout stays failed. A plugin's dirty mark
|
|
226
|
+
clears only when it applied on every host of every step, and a kind built in the rollout that no step shipped keeps its
|
|
227
|
+
plugins dirty.
|
|
228
|
+
|
|
229
|
+
## 5. Not in slice 10
|
|
230
|
+
|
|
231
|
+
The web page host, the browser's evaluation of scripts and serving them (slice 11); the TUI (slice 11); relocation and
|
|
232
|
+
pause of workers (the app's: `start(address)` is where the app awaits its own barrier); request resolvers and
|
|
233
|
+
authenticators as registries (the app's); chunked transfers as a Chord helper (an owner pattern, design §23); the
|
|
234
|
+
channel component (D6); telemetry spans for requests (slice 9).
|
|
235
|
+
|
|
236
|
+
## 6. Kernel changes
|
|
237
|
+
|
|
238
|
+
- `host.route(Kind, options, ctx)` returns a `RouteHandle { retry(address), suspend(address) }` (D10b, R1, Q3).
|
|
239
|
+
- A route has no backoff timer (R2): a rejected `resolve`, or a link that closes before its handshake completed, fails
|
|
240
|
+
the route's holders `failed { waiting }` with its error (they stay on the address's path, Q1) and resolves nothing
|
|
241
|
+
more; the route resolves again only for a new hold, on `retry`, or once after a link that had completed its handshake
|
|
242
|
+
dropped while holds exist; a suspended address resolves nothing while it has holders and forgets the suspension with
|
|
243
|
+
the last one.
|
|
244
|
+
- `host.root(fn, ctx, { principal: undefined })` runs anonymous (`ctx.principal` undefined); only an omitted
|
|
245
|
+
`principal` takes the host's (a public-API change of `RootOptions`: `"principal" in options`).
|
|
246
|
+
- Origins only from relays (decision, 2026-10-31, security audit): `origin` is read only on parent and route links;
|
|
247
|
+
on a child link it is a protocol error; a per-origin record goes once it has no holds, calls or messages in flight.
|
|
248
|
+
- An anonymous `host.root` scope gets no remote object served on its host (`forbidden`).
|
|
249
|
+
- Design §3: the paragraph on a `resolve` waiting for the worker's `load` is updated (done), and "Chord never closes a
|
|
250
|
+
route link itself except at `host.close`" becomes "except at `host.close`, the heartbeat and protocol errors".
|
|
251
|
+
|
|
252
|
+
## 7. Tests, examples, cost
|
|
253
|
+
|
|
254
|
+
- One test file per source file. HTTP: listening before the publication (a request waits, then is served; 503 past
|
|
255
|
+
`readyTimeout`); the router resolved once; `Host` (loopback forms, allowed from the option and from a writable
|
|
256
|
+
contribution changing at runtime) and `Origin` checks; anonymous requests (`ctx.principal` undefined on a server with
|
|
257
|
+
a principal); authentication per request and upgrade; throwing hooks; patterns, decoding, precedence, `HEAD`, 404;
|
|
258
|
+
conflicts reported; wraps (phases, order, scopes, a missing route, a throwing wrap); every response kind (ranges
|
|
259
|
+
206/416, multi-range, `HEAD`, download names, chunked without `size`, a short `read`, a disconnect stopping `read`,
|
|
260
|
+
header merging); `json`/`body` limits and their statuses, an unread body; a throwing route; the links endpoint
|
|
261
|
+
(`host.accept`, options per socket, a refused socket, the output cap); plugin sockets (an oversized frame closes only
|
|
262
|
+
that socket; a reload removing the contribution closes its sockets 1012); a removed allowed host closing its
|
|
263
|
+
connections; a reload swapping a route while a request runs on the old router; `fileResponse` closing its handle on
|
|
264
|
+
every path; `close` (refusals, waiting, then sealing). Workers: a start on the first hold; the link made at the fork
|
|
265
|
+
(a load longer than the heartbeat's 30 s binds); `started` on `connect`; the start timer covering only the start (a
|
|
266
|
+
slow `load` is not killed); the lock (an old worker's lock waited for without polling); `start` throwing; a crash,
|
|
267
|
+
a `fatal`, a link lost with a live child (killed after the grace), and a failed start each staying failed (no fork for
|
|
268
|
+
a new hold; holders `failed { waiting }`), `retry` and a rollout clearing them and rebinding the holders; idle stop,
|
|
269
|
+
a worker idle from its start, a hold keeping it; `busy`; 1,000 sequential local calls sending no message;
|
|
270
|
+
`stop` with holders (suspended, then forgotten with the last holder); stop during start and start during stop; a
|
|
271
|
+
crash while a resolve is queued; `close` (cancelling a start waiting in `start(address)`, no `worker_failed` at
|
|
272
|
+
shutdown); the rollout step (complete sets, a worker starting during it, a clean stop listed `stopped`, a crash during
|
|
273
|
+
it, an aborted reload's late reply, a kind no step shipped staying dirty); the child's shutdown during its startup,
|
|
274
|
+
disconnect and signals; `redact` (a secret in the original message reaches neither `fatal` nor `state`). Kernel:
|
|
275
|
+
`RouteHandle.retry` and `suspend`; a link closing before its handshake (no re-resolve loop); no re-resolve without a
|
|
276
|
+
hold, a retry or one drop of a healthy link; `host.root` with an explicit `undefined` principal.
|
|
277
|
+
- GC: requests (their scopes, bodies, file handles) and stopped workers' state collected; no listener left on a closed
|
|
278
|
+
socket; 1,000 addresses started and stopped leave no `state` entries, no per-address queue records and no
|
|
279
|
+
suspensions.
|
|
280
|
+
- Examples (each a folder, as 7c's): `40-http` (routes, a wrap, a download with ranges, a socket, an allowed host
|
|
281
|
+
added at runtime), `41-workers` (a server routing a session kind to forked workers, idle stop, a crash staying failed
|
|
282
|
+
until Retry, a rollout reaching the workers and restarting a held failed one).
|
|
283
|
+
- Benchmark: a route's overhead per request against a plain Node handler (bar: within 10% of a keep-alive round
|
|
284
|
+
trip; Opus measured 2.5 µs of 54 µs); `bytes` throughput for a 100 MiB file against `pipeline`; a worker's cold start
|
|
285
|
+
(fork to `started` and to `ready`) with shipped scripts; memory per idle worker on the server.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Slice 11: web and TUI surfaces (spec)
|
|
2
|
+
|
|
3
|
+
Status: **draft** (owner, 2026-10-30: "start on slice 11"; "this one needs to be generic, so apps can do whatever they
|
|
4
|
+
want: css, tui components, themes"). Points marked **Decision needed** go to the owner one at a time.
|
|
5
|
+
|
|
6
|
+
Slice 11 builds the generic plumbing of the app kit's two client surfaces (decision "Slice 7b's open points" (6)):
|
|
7
|
+
`chord/next/web` (a browser page as a Chord client host that loads the plugins' web facets the server built) and
|
|
8
|
+
`chord/next/tui` (a terminal client that does the same for TUI facets). The app keeps everything visible: its HTML
|
|
9
|
+
shell, CSS, components, themes, navigation, layout, keys. Built on slice 10 (`http` serves, the plugins component
|
|
10
|
+
builds and ships) and 7c (scripts as plain data, `createPluginRuntime`). Rule "The app kit is malleable by plugins"
|
|
11
|
+
applies.
|
|
12
|
+
|
|
13
|
+
Read in full before writing code: this spec; `decisions.md` ("Slice 7c's points", "Slice 10's points"); `design.md`
|
|
14
|
+
§3 (clients), §21, §23; `slice-10.md`; p2's `src/hosts/web.ts`, `src/hosts/web/main.tsx`, `src/web/reconnect.ts`,
|
|
15
|
+
`src/runtime/web.tsx`, `src/hosts/bundles.ts`, `src/hosts/tui.ts`, `src/hosts/tui-client.ts`, `src/hosts/tui-sandbox.ts`,
|
|
16
|
+
`src/hosts/tui-bootstrap.mjs`, `src/hosts/tui-worker-runtime.ts`.
|
|
17
|
+
|
|
18
|
+
## 1. What p2 has, and what is generic
|
|
19
|
+
|
|
20
|
+
| p2 today | Generic (slice 11) | The app's |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| one esbuild bundle per surface: runtime, React, every plugin's web facet, CSS; the page reloads whole when its version changes | a base bundle per surface (host packages and the surface runtime) plus each plugin's facet script (7c); new scripts applied in place | which host packages a surface has |
|
|
23
|
+
| the page: HTML with a CSP nonce, a theme script, `#root`, the bundle | serving the base bundle, scripts, stylesheets and assets through `http`; a page template hook | the HTML shell, CSP, theme, manifest and icons, service worker |
|
|
24
|
+
| `startWeb`: a client host, `keepConnected` (reconnect with backoff, wake on visibility and network change, stale-socket checks), protocol mismatch reloads the page | `startPage`: the client host, the connection loop, facet loading and reloading | `canRetry` policy (an ended share), connection UI |
|
|
25
|
+
| `runtime/web.tsx`: `HostContext`, `useObject`, `useReadable`, `useRegistry`, slots, sheets, navigation, owner state | optional React bindings: provider, hold, watch, registry, reference state | slots, sheets, navigation, everything else |
|
|
26
|
+
| TUI: a trusted launcher (auth, terminal, local preferences, actions), a permission-dropped worker thread running the downloaded bundle, a bridge terminal, a link through the launcher | a Node client surface: connect, download the base bundle and TUI scripts, evaluate, reload in place; the sandbox as an option | the TUI's components (pi-tui or other), commands, keys, themes, launcher actions |
|
|
27
|
+
|
|
28
|
+
## 2. `chord/next/web`
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// server: serve the page surface (routes contributed to `Http`, so they reload like any contribution)
|
|
32
|
+
const web = webSurface(server, {
|
|
33
|
+
kind: "web",
|
|
34
|
+
plugins, // the plugins component: the web kind's scripts
|
|
35
|
+
hostPackages: { "@earendil-works/chord/next": "chord-next", react: "react", "react/jsx-runtime": "react-jsx", "@app/web": "app-web" },
|
|
36
|
+
base: "./src/web/base.ts", // the surface runtime's entry: built once into the base bundle
|
|
37
|
+
page: (request, ctx, assets) => appShell(assets), // the app's HTML; `assets` lists the base script and stylesheets
|
|
38
|
+
}, ctx);
|
|
39
|
+
|
|
40
|
+
// browser: src/web/base.ts (in the base bundle)
|
|
41
|
+
startPage({
|
|
42
|
+
principal: { kind: "server" },
|
|
43
|
+
connect: () => new WebSocket(`${location.origin.replace(/^http/, "ws")}/chord`),
|
|
44
|
+
hostPackages: { "@earendil-works/chord/next": chordNext, react: React, "react/jsx-runtime": jsx, "@app/web": appWeb },
|
|
45
|
+
core: [{ name: "@app/web", facet: webCore }],
|
|
46
|
+
canRetry: async () => !(await shareEnded()),
|
|
47
|
+
onConnected: (connected) => setBanner(!connected),
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Decision needed P1: scope.** What `chord/next/web` ships: serving the surface (base bundle, scripts, stylesheets,
|
|
52
|
+
> assets, the page through an app template), the browser's client host with its connection loop, loading the plugins'
|
|
53
|
+
> web facets and applying new ones. Not shipped: HTML, CSS, components, themes, navigation, slots. React bindings
|
|
54
|
+
> separately (P7).
|
|
55
|
+
|
|
56
|
+
> **Decision needed P2: how the browser evaluates a facet script.** (a) Each script served as a file wrapped to register
|
|
57
|
+
> itself (`chordFacet("<plugin>", "<digest>", function (require, module, exports) { … })`), loaded with `<script
|
|
58
|
+
> src>` under a `'self'` CSP; (b) fetched and run with `new Function` (needs `'unsafe-eval'`).
|
|
59
|
+
|
|
60
|
+
> **Decision needed P3: the base bundle.** Built on the server per surface by esbuild from the app's `base` entry with
|
|
61
|
+
> the host packages inlined once; content-hashed; a change of base code reloads the page (as a server restart).
|
|
62
|
+
|
|
63
|
+
> **Decision needed P4: applying new scripts in a page.** (a) In place: the page's host reloads per plugin
|
|
64
|
+
> (`host.reload`, 7b), the UI re-renders from its registries; (b) the whole page reloads, as p2.
|
|
65
|
+
|
|
66
|
+
> **Decision needed P5: how a page learns of new scripts.** (a) Through Chord: the surface serves a `remote` object
|
|
67
|
+
> whose state is the current set (digests and URLs); every page holds it and applies changes; the rollout step publishes
|
|
68
|
+
> without waiting for pages; (b) polling a manifest (p2: every 5 s while visible).
|
|
69
|
+
|
|
70
|
+
> **Decision needed P6: the connection loop.** p2's `keepConnected`: reconnect after a drop with backoff (250 ms to 5
|
|
71
|
+
> s), wake on visibility, `online` and network change, replace a stale socket, stop on an outdated protocol (reload) or
|
|
72
|
+
> when the app's `canRetry` says no. A network reconnect, not a crash restart.
|
|
73
|
+
|
|
74
|
+
> **Decision needed P7: React bindings.** An optional subpath (`chord/next/react`) with `HostProvider`,
|
|
75
|
+
> `useHold(Type, params)`, `useWatch(watchable)`, `useRegistry(R)`, `useRefState(ref)`; or none in Chord.
|
|
76
|
+
|
|
77
|
+
## 3. `chord/next/tui`
|
|
78
|
+
|
|
79
|
+
> **Decision needed P8: scope.** A Node client surface (`startTerminal`): connect over WebSocket with the app's
|
|
80
|
+
> credentials, download the base bundle and the TUI scripts from the server, evaluate them, run the app's entry with
|
|
81
|
+
> its host, apply new scripts in place; the terminal belongs to the app. p2's sandbox (a permission-dropped worker
|
|
82
|
+
> thread for code from another person's server, a bridge terminal, launcher actions) as an option or not in Chord.
|
|
83
|
+
|
|
84
|
+
> **Decision needed P9: pi-tui bindings.** None in Chord (the app chooses its TUI library), or optional helpers.
|
|
85
|
+
|
|
86
|
+
## 4. Not in slice 11
|
|
87
|
+
|
|
88
|
+
App shells, themes, navigation, slots, offline support (service workers), the TUI's commands and preferences;
|
|
89
|
+
telemetry (slice 9).
|
|
90
|
+
|
|
91
|
+
## 5. Tests, examples, cost
|
|
92
|
+
|
|
93
|
+
Filled once the points are decided. Examples: small and cute (owner), each a folder.
|