oxianjs 0.21.3 → 0.22.0-next.29ff900

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +14 -0
  3. package/docs/migration-0.22.md +70 -0
  4. package/docs/owner-retirement.md +122 -0
  5. package/package.json +44 -9
  6. package/src/adapters/node/connection-budget.d.ts +17 -0
  7. package/src/adapters/node/connection-budget.js +34 -0
  8. package/src/adapters/node/index.d.ts +4 -0
  9. package/src/adapters/node/index.js +2 -0
  10. package/src/adapters/node/server.d.ts +4 -0
  11. package/src/adapters/node/server.js +178 -0
  12. package/src/adapters/node/types.d.ts +7 -0
  13. package/src/app/application.d.ts +6 -0
  14. package/src/app/application.js +227 -0
  15. package/src/app/base_path.d.ts +11 -0
  16. package/src/app/base_path.js +31 -0
  17. package/src/app/core.d.ts +4 -0
  18. package/src/app/core.js +4 -0
  19. package/src/app/middleware.d.ts +8 -0
  20. package/src/app/middleware.js +52 -0
  21. package/src/app/response.d.ts +10 -0
  22. package/src/app/response.js +102 -0
  23. package/src/app/sse.d.ts +7 -0
  24. package/src/app/sse.js +168 -0
  25. package/src/app/types.d.ts +83 -0
  26. package/src/app/types.js +1 -0
  27. package/src/core.d.ts +15 -0
  28. package/src/core.js +13 -0
  29. package/src/http/body.d.ts +1 -0
  30. package/src/http/body.js +69 -0
  31. package/src/http/gateway.d.ts +2 -0
  32. package/src/http/gateway.js +149 -0
  33. package/src/http/index.d.ts +5 -0
  34. package/src/http/index.js +5 -0
  35. package/src/http/metadata.d.ts +20 -0
  36. package/src/http/metadata.js +259 -0
  37. package/src/http/types.d.ts +34 -0
  38. package/src/http/types.js +3 -0
  39. package/src/http/workload.d.ts +3 -0
  40. package/src/http/workload.js +61 -0
  41. package/src/hypervisor/config.d.ts +29 -0
  42. package/src/hypervisor/config.js +123 -0
  43. package/src/hypervisor/hypervisor.d.ts +10 -0
  44. package/src/hypervisor/hypervisor.js +449 -0
  45. package/src/hypervisor/index.d.ts +3 -0
  46. package/src/hypervisor/index.js +2 -0
  47. package/src/hypervisor/internal/admission.d.ts +25 -0
  48. package/src/hypervisor/internal/admission.js +117 -0
  49. package/src/hypervisor/internal/connection.d.ts +33 -0
  50. package/src/hypervisor/internal/connection.js +199 -0
  51. package/src/hypervisor/internal/directory.d.ts +16 -0
  52. package/src/hypervisor/internal/directory.js +51 -0
  53. package/src/hypervisor/internal/dispatch-queue.d.ts +35 -0
  54. package/src/hypervisor/internal/dispatch-queue.js +204 -0
  55. package/src/hypervisor/internal/dispatch.d.ts +20 -0
  56. package/src/hypervisor/internal/dispatch.js +171 -0
  57. package/src/hypervisor/internal/drain.d.ts +15 -0
  58. package/src/hypervisor/internal/drain.js +102 -0
  59. package/src/hypervisor/internal/ephemeral.d.ts +11 -0
  60. package/src/hypervisor/internal/ephemeral.js +64 -0
  61. package/src/hypervisor/internal/lifecycle.d.ts +32 -0
  62. package/src/hypervisor/internal/lifecycle.js +189 -0
  63. package/src/hypervisor/internal/listener.d.ts +6 -0
  64. package/src/hypervisor/internal/listener.js +17 -0
  65. package/src/hypervisor/internal/model.d.ts +88 -0
  66. package/src/hypervisor/internal/model.js +1 -0
  67. package/src/hypervisor/internal/primitives.d.ts +27 -0
  68. package/src/hypervisor/internal/primitives.js +149 -0
  69. package/src/hypervisor/internal/session.d.ts +34 -0
  70. package/src/hypervisor/internal/session.js +228 -0
  71. package/src/hypervisor/internal/work-stream.d.ts +34 -0
  72. package/src/hypervisor/internal/work-stream.js +676 -0
  73. package/src/hypervisor/types.d.ts +162 -0
  74. package/src/hypervisor/types.js +1 -0
  75. package/src/lifecycle/index.d.ts +1 -0
  76. package/src/lifecycle/index.js +1 -0
  77. package/src/lifecycle/types.d.ts +193 -0
  78. package/src/lifecycle/types.js +1 -0
  79. package/src/mod.d.ts +7 -0
  80. package/src/mod.js +7 -0
  81. package/src/protocol/binary.d.ts +40 -0
  82. package/src/protocol/binary.js +182 -0
  83. package/src/protocol/control.d.ts +47 -0
  84. package/src/protocol/control.js +630 -0
  85. package/src/protocol/flow_control.d.ts +27 -0
  86. package/src/protocol/flow_control.js +66 -0
  87. package/src/protocol/index.d.ts +7 -0
  88. package/src/protocol/index.js +7 -0
  89. package/src/protocol/limits.d.ts +14 -0
  90. package/src/protocol/limits.js +7 -0
  91. package/src/protocol/state.d.ts +60 -0
  92. package/src/protocol/state.js +542 -0
  93. package/src/protocol/violation.d.ts +11 -0
  94. package/src/protocol/violation.js +35 -0
  95. package/src/providers/cloud_run_jobs.d.ts +49 -0
  96. package/src/providers/cloud_run_jobs.js +1213 -0
  97. package/src/providers/conformance.d.ts +22 -0
  98. package/src/providers/conformance.js +104 -0
  99. package/src/providers/contract.d.ts +35 -0
  100. package/src/providers/contract.js +258 -0
  101. package/src/providers/core.d.ts +5 -0
  102. package/src/providers/core.js +5 -0
  103. package/src/providers/externally_attached.d.ts +49 -0
  104. package/src/providers/externally_attached.js +167 -0
  105. package/src/providers/types.d.ts +69 -0
  106. package/src/providers/types.js +1 -0
  107. package/src/router/types.d.ts +71 -0
  108. package/src/router/types.js +15 -0
  109. package/src/supervisor/credentials.d.ts +48 -0
  110. package/src/supervisor/credentials.js +259 -0
  111. package/src/supervisor/dispatcher.d.ts +81 -0
  112. package/src/supervisor/dispatcher.js +522 -0
  113. package/src/supervisor/index.d.ts +6 -0
  114. package/src/supervisor/index.js +5 -0
  115. package/src/supervisor/internal.d.ts +15 -0
  116. package/src/supervisor/internal.js +121 -0
  117. package/src/supervisor/sessions.d.ts +60 -0
  118. package/src/supervisor/sessions.js +334 -0
  119. package/src/supervisor/state.d.ts +29 -0
  120. package/src/supervisor/state.js +128 -0
  121. package/src/supervisor/store.d.ts +32 -0
  122. package/src/supervisor/store.js +144 -0
  123. package/src/supervisor/types.d.ts +119 -0
  124. package/src/supervisor/types.js +1 -0
  125. package/src/transport/declarations.d.ts +29 -0
  126. package/src/transport/declarations.js +1 -0
  127. package/src/transport/frame.d.ts +44 -0
  128. package/src/transport/frame.js +335 -0
  129. package/src/transport/in-process.d.ts +42 -0
  130. package/src/transport/in-process.js +276 -0
  131. package/src/transport/index.d.ts +5 -0
  132. package/src/transport/index.js +4 -0
  133. package/src/transport/queue.d.ts +19 -0
  134. package/src/transport/queue.js +83 -0
  135. package/src/transport/socket.d.ts +6 -0
  136. package/src/transport/socket.js +102 -0
  137. package/src/transport/types.d.ts +104 -0
  138. package/src/transport/types.js +1 -0
  139. package/src/transport/websocket.d.ts +13 -0
  140. package/src/transport/websocket.js +506 -0
  141. package/src/work/index.d.ts +1 -0
  142. package/src/work/index.js +1 -0
  143. package/src/work/types.d.ts +27 -0
  144. package/src/work/types.js +1 -0
  145. package/src/worker/backoff.d.ts +14 -0
  146. package/src/worker/backoff.js +46 -0
  147. package/src/worker/index.d.ts +3 -0
  148. package/src/worker/index.js +3 -0
  149. package/src/worker/internal/async.d.ts +11 -0
  150. package/src/worker/internal/async.js +132 -0
  151. package/src/worker/internal/credentials.d.ts +32 -0
  152. package/src/worker/internal/credentials.js +118 -0
  153. package/src/worker/internal/errors.d.ts +5 -0
  154. package/src/worker/internal/errors.js +40 -0
  155. package/src/worker/internal/execution_ledger.d.ts +13 -0
  156. package/src/worker/internal/execution_ledger.js +42 -0
  157. package/src/worker/internal/lifecycle.d.ts +44 -0
  158. package/src/worker/internal/lifecycle.js +120 -0
  159. package/src/worker/internal/session-options.d.ts +40 -0
  160. package/src/worker/internal/session-options.js +1 -0
  161. package/src/worker/internal/validation.d.ts +2 -0
  162. package/src/worker/internal/validation.js +19 -0
  163. package/src/worker/internal/work.d.ts +8 -0
  164. package/src/worker/internal/work.js +41 -0
  165. package/src/worker/session.d.ts +7 -0
  166. package/src/worker/session.js +1456 -0
  167. package/src/worker/types.d.ts +162 -0
  168. package/src/worker/types.js +1 -0
  169. package/src/worker/worker.d.ts +10 -0
  170. package/src/worker/worker.js +373 -0
  171. package/src/host/host.d.ts +0 -5
  172. package/src/host/host.js +0 -84
  173. package/src/host/index.d.ts +0 -3
  174. package/src/host/index.js +0 -2
  175. package/src/host/types.d.ts +0 -46
  176. package/src/host/types.js +0 -2
  177. package/src/owner/index.d.ts +0 -6
  178. package/src/owner/index.js +0 -4
  179. package/src/owner/memory.d.ts +0 -15
  180. package/src/owner/memory.js +0 -216
  181. package/src/owner/owner.d.ts +0 -3
  182. package/src/owner/owner.js +0 -462
  183. package/src/owner/primitives.d.ts +0 -9
  184. package/src/owner/primitives.js +0 -51
  185. package/src/owner/remote.d.ts +0 -44
  186. package/src/owner/remote.js +0 -175
  187. package/src/owner/types.d.ts +0 -203
  188. /package/src/{owner → adapters/node}/types.js +0 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # oxianjs
2
+
3
+ ## 0.22.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Prepare the 0.22 prerelease line with a breaking removal of the unused generic
8
+ owner, owner-host and Postgres-owner APIs. Durable ownership moves to the
9
+ concrete Copilotz runtime, whose replacement implementation remains pending.
10
+
11
+ Retain worker dispatch, transports, sessions and independent Node connection
12
+ budgeting. Publish the existing Node-portable root and explicit subpaths, with
13
+ installed-package type and in-process/WebSocket proofs; remove the owner-only
14
+ PostgreSQL dependency and CI service.
15
+
16
+ This base version is used for merge-triggered `0.22.0-next.<mergeSHA>` builds
17
+ on npm's `next` tag and the same-version JSR mirror. It does not authorize a
18
+ stable `v0.22.0` tag or move npm's `latest` tag. See the migration and
19
+ retirement documentation for removed APIs, supported exports and preserved
20
+ conformance evidence.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # Oxian for Node
2
+
3
+ Fetch-native applications and streaming work over in-process or WebSocket transports.
4
+
5
+ Requires Node 24 or newer. This package exports the portable root, http, hypervisor,
6
+ protocol, providers, transport, work, worker, and adapters/node. The providers
7
+ subpath contains only portable providers; it does not include local processes.
8
+
9
+ The filesystem app factories, config loader, file router, local composition, edge,
10
+ Deno adapter, and CLI remain available only in the Deno/JSR package during the
11
+ Node migration. The retired owner, host, and PostgreSQL-owner APIs are not included;
12
+ see the [retirement and migration notes](docs/owner-retirement.md).
13
+
14
+ See https://github.com/AxionCompany/oxian-js for documentation.
@@ -0,0 +1,70 @@
1
+ # Migrating to the 0.22 prerelease
2
+
3
+ The 0.22.0 source base introduces a breaking removal of unused owner APIs.
4
+ Successful verification on `main` publishes `0.22.0-next.<mergeSHA>` to npm's
5
+ `next` tag and mirrors the exact same version to JSR. This is a prerelease:
6
+ `latest` stays at 0.21.3, and no `v0.22.0` stable tag is created by this work.
7
+
8
+ Use `npm install oxianjs@next` to try the new Node package. For a reproducible
9
+ installation, pin the exact returned version. JSR users should likewise select
10
+ the matching published next version rather than assume a stable 0.22.0 exists.
11
+ Current source API examples use the base version; substitute that next version
12
+ when installing from the registry.
13
+
14
+ ## Removed APIs
15
+
16
+ The `/owner`, `/host`, and `/adapters/postgres` package entrypoints no longer
17
+ exist. This removes `createOwner`, `createMemoryOwnerStore`, command/snapshot
18
+ sessions, `createOwnerHost`, shard maps and the Postgres-owner store. The
19
+ standalone implementation was unused by applications. It is not retained behind
20
+ aliases or compatibility stubs, and importing those entrypoints now fails.
21
+
22
+ A downstream experiment that still uses these APIs must remain pinned to its
23
+ previous version or preserved checkout until its concrete replacement is ready.
24
+ The full Git history and the behavioral migration inventory remain available;
25
+ see [retirement evidence](owner-retirement.md). No database data is deleted or
26
+ migrated by upgrading the package.
27
+
28
+ Copilotz will own its durable command and storage runtime. That replacement,
29
+ authenticated reverse calls, real-table group commits, recovery and deployment
30
+ integration are not implemented by this release. Removing the library does not
31
+ certify those guarantees or add worker-to-owner invocation to the protocol.
32
+
33
+ ## Retained Node surface
34
+
35
+ The npm build now exposes:
36
+
37
+ - `oxianjs`: the portable application, HTTP, Hypervisor, worker and work core
38
+ - `/adapters/node`: the Node HTTP/WebSocket listener and connection budget
39
+ - `/http`, `/hypervisor`, `/protocol`, `/transport`, `/work`, `/worker`
40
+ - `/providers`: the portable provider contract, externally attached provider,
41
+ Cloud Run Jobs provider and conformance helpers
42
+
43
+ Node 24 or newer is required. `ws` is the Node listener's runtime dependency;
44
+ Postgres and its type dependency have been removed. Importing the portable root
45
+ still does not create listeners, spawn processes or require a database.
46
+
47
+ The npm `/providers` subpath excludes the Deno local-process provider. The
48
+ Deno-bound `/app`, `/config`, `/local`, `/router`, `/edge`, `/cli`, `/bin`, and
49
+ `/adapters/deno` remain available only in the retained JSR surface during the
50
+ Node migration. The npm build is deliberately not advertised as full JSR parity.
51
+
52
+ ## Node listener migration
53
+
54
+ `serve({ hypervisor })` still defaults to a 1,000-connection budget. To share a
55
+ lower limit between listeners, import `createConnectionBudget` from
56
+ `oxianjs/adapters/node` and pass the same value as each listener's `budget`.
57
+ This replaces the old owner-host object for that purpose. Budget acquisition
58
+ refuses excess upgrades with HTTP 503; socket closure releases the lease once,
59
+ including for rejected upgrades. Closing a listener does not close an injected
60
+ budget or the application-owned Hypervisor.
61
+
62
+ ## Verification
63
+
64
+ The release retains Oxian's protocol, supervisor, transport, worker and
65
+ Hypervisor suites. CI checks Node and Deno plus an installed npm tarball:
66
+ public exports, missing retired subpaths, declaration consumption, absence of
67
+ tests/raw TypeScript in the npm payload, and real in-process and Node WebSocket
68
+ work dispatch. Historical owner tests remain migration evidence, not executable
69
+ replacement coverage. Stable publication still requires its separate exact-tag
70
+ workflow and is outside this prerelease.
@@ -0,0 +1,122 @@
1
+ # Durable owner retirement: migration evidence
2
+
3
+ Status: owner API retirement, 2026-10-16. This is an inventory of preserved
4
+ requirements and historical evidence, not a replacement implementation or a
5
+ claim that Copilotz owner conformance passes.
6
+
7
+ ## Release and compatibility
8
+
9
+ This change prepares the 0.22.0 base for `0.22.0-next.<mergeSHA>` prereleases.
10
+ The successful main verification workflow publishes npm `next` and the matching
11
+ JSR version. It leaves npm `latest` at 0.21.3. No stable tag is part of this
12
+ release. See [the migration notes](migration-0.22.md) for the removed entrypoints
13
+ and retained transport boundary.
14
+
15
+ ## Boundary
16
+
17
+ Oxian keeps its portable application/worker/Hypervisor, transports, dispatch,
18
+ session fencing, admission hooks and protocol. The unused generic owner,
19
+ owner-host and Postgres-owner API is removed. Durable command handling and
20
+ application storage belong in the concrete Copilotz runtime. The approved
21
+ worker-originated owner-call extension is still to be specified and built.
22
+ There is no retained test-only owner runtime or placeholder owner API.
23
+
24
+ The Node listener's connection limiter remains, independently of ownership.
25
+ It can be shared across listeners, refuses overload, and releases leases on
26
+ socket closure. Its active tests live beside the adapter.
27
+
28
+ ## Immutable recovery points
29
+
30
+ - Public main before retirement:
31
+ `6cf55dc13834bafcfe9ef4c4067372b68982ae2f`
32
+ ([source](https://github.com/AxionCompany/oxian-js/tree/6cf55dc13834bafcfe9ef4c4067372b68982ae2f))
33
+ - Preserved local owner-records work:
34
+ `2dfebacf3408c0e25672289ec31e7201588d6cca`, on the separate recovery branch
35
+ and in the transferred Git bundle. This commit is not a public release.
36
+ - Copilotz's unpushed parallel-kernel work and uncommitted wire changes remain
37
+ in their original checkout. Their behavioral cases are reference material;
38
+ the new runtime must use Copilotz's real tables, not adopt `owner_records`.
39
+
40
+ Use `git show <commit>:<path>` to recover an exact historical test. The retirement
41
+ branch starts at public main; it neither cherry-picks nor deletes the unpublished
42
+ owner-records commit. No existing database is modified by deleting these APIs.
43
+
44
+ ## Test inventory
45
+
46
+ At the public-main recovery point, five files declare 46 cases:
47
+
48
+ | Historical path | Cases | Disposition |
49
+ |---|---:|---|
50
+ | `tests/owner/owner_test.ts` | 18 | Receipt/command cases for the concrete Copilotz owner |
51
+ | `tests/owner/sequence_test.ts` | 9 | Sequence/alarm cases for the real event log |
52
+ | `tests/owner/remote_test.ts` | 5 | Snapshot/recovery cases for authenticated worker calls |
53
+ | `tests/owner/postgres_test.ts` | 10 | Real-table transaction/fence cases |
54
+ | `tests/host/host_test.ts` | 4 | Two ownership cases remain migration work; two connection-budget cases rehomed |
55
+
56
+ `tests/owner/postgres_database.ts` was the test helper, not a conformance case.
57
+ Its Mac-specific local-cluster setup and the owner-only CI database service are
58
+ removed with the suites. The local owner-records commit adds three Postgres
59
+ cases, bringing its historical total to 49. Removing executable tests here
60
+ must not be reported as completion of their replacement coverage.
61
+
62
+ ## Behavioral cases to port
63
+
64
+ The destination is tests next to Copilotz's concrete owner and storage code,
65
+ using existing domain/event tables. The F5/F7/F9 contracts and guarantee map
66
+ remain the acceptance bar. This checklist is evidence, not the whole plan.
67
+
68
+ | Guarantee | Historical tests or fixture | Replacement requirement |
69
+ |---|---|---|
70
+ | Result, effects and receipt are atomic | `owner_test.ts`: “a committed command returns its result and keeps its receipt” | One fenced outer transaction on real tables |
71
+ | Compatible replay, conflicting reuse, identity domains | “the same request returns its receipt; a different request is rejected”; “identity domains do not share receipts” | Stable logical intent, distinct incarnation/occurrence keys and separate current authority |
72
+ | Command-local rollback and timestamp | “one failing command rolls back only itself”; “a thrown command rolls back its writes and keeps the rest of the group”; “a lease crossed inside a group fails only the later command” | A savepoint and timestamp per command; no partial effects or publication |
73
+ | Unknown versus definitive commit outcome | “a lost commit stops the owner and the successor trusts receipts”; “a lost commit that aborted reruns each command once”; “a definitive rollback retries each command on its own” | Successor waits for server outcome; receipt recovery before preparing a different effect |
74
+ | Outer output gate | “post-commit callbacks run only after the commit is durable”; “a failed command does not run its post-commit callback” | Results, materialization, dispatch and notifications follow the outer commit |
75
+ | Admission and bounded queue | “admission admits 32 and recovers a receipt at capacity”; “a full queue refuses and a stopped owner reports ownership lost” | Atomic cap and bounded refusal; receipt recovery at capacity |
76
+ | Input/result isolation | “a command runs the input that was submitted”; “changing a returned receipt does not change the recorded one”; “commands in one group keep their own results” | Caller mutation cannot change accepted intent or recorded outcome |
77
+ | Epoch fencing | “a stale owner fails once a successor acquires the epoch”; Postgres takeover/open-transaction/assignment cases | Fence actual writes; takeover blocks behind unresolved transaction; routing alone is not authority |
78
+ | Mixed group uncertainty | `postgres_test.ts` lost-commit fixture mixes compatible duplicate, conflicting duplicate, success and failure, under committed/aborted outcomes | Keep this matrix and add actual in-flight COMMIT uncertainty |
79
+ | Failed transaction cleanup | Postgres failed-BEGIN/rollback case | Discard an unusable pooled connection |
80
+ | Receipt recovery without stale mutation | `remote_test.ts`: “an old session cannot mutate after takeover and can still read receipts” | Authenticated, owner-scoped same-worker recovery; no implicit protected-value hydration |
81
+ | Snapshot lifecycle | Remaining four `remote_test.ts` cases | Repeatable snapshot, cancellation, takeover, idle/lifetime expiry and in-flight failure; bounded sessions |
82
+ | Gap-free event sequence | `sequence_test.ts`: “a failed command and a duplicate consume no sequence”; “a rolled-back group consumes no sequence” | Existing `events.seq` shares effects/receipts/fence commit |
83
+ | Atomic attach and repair | “attach captures the head of its commit”; “a heartbeat head shows a dropped last update”; “a dropped push resets that observer” | Real push transport, reset and heartbeat-head repair |
84
+ | Durable alarms | Remaining four `sequence_test.ts` cases; Postgres alarm revision/sequence takeover cases | Wake after restart, early-timer guard, transient-read retry, rollback cleanup and actual scheduler semantics |
85
+ | Placement | `host_test.ts`: “a shard that does not own an id does not move its epoch”; “one process runs every owner assigned to it” | Named-owner routing and host-specific activation; no ownership claim from process count alone |
86
+ | Node overload protection | Former final two `host_test.ts` cases | Active `src/adapters/node/connection-budget.test.ts`; no owner-host dependency |
87
+
88
+ The three extra `2dfebac` cases assert that commit-after-rollback cannot persist
89
+ writes; committed records survive takeover while rolled-back writes do not;
90
+ and records are owner-isolated with bounded read-only snapshots. Preserve those
91
+ invariants against Copilotz tables. Generic map insertion order and the
92
+ `owner_records` schema are not requirements on domain storage.
93
+
94
+ ## Limits of the old evidence
95
+
96
+ - Lost COMMIT responses were injected after completed commit/rollback. Killing
97
+ an open transaction does not exercise an owner dying during COMMIT.
98
+ - Remote-session tests use local helpers, not authenticated worker RPC or the
99
+ combined worker, connection, owner and execution-attempt fences.
100
+ - Observer tests manually acknowledge/reset; real dropped transport messages,
101
+ subscriber authorization and end-to-end repair remain to prove.
102
+ - Alarm tests do not establish exact-due schedule claims, occurrence identity,
103
+ misfire skipping, or paused/manual-run semantics.
104
+ - These files do not establish fresh stream allocation across attempts,
105
+ protected-value grants, two-phase settlement, await/resume, effect
106
+ reconciliation, full activation readiness, or saturated HTTP/Action progress.
107
+
108
+ These missing cases remain work. Retirement neither drops them nor marks them
109
+ passing. Durable Object activation/storage and production routing remain
110
+ host-specific work; deleting the old library does not implement either.
111
+
112
+ ## Evidence that remains active in Oxian
113
+
114
+ All protocol, supervisor, transport, worker and Hypervisor suites remain.
115
+ In particular, `tests/protocol/*` and `tests/supervisor/*` retain their 95 declared
116
+ cases for byte/sequence credit, capacity reservation, cancellation, drain,
117
+ half-close, session generations, credential resume, durable acceptance and
118
+ committed-versus-indeterminate dispatch. The installed npm-package proof now
119
+ uses retained worker dispatch instead of the deleted owner/host API.
120
+
121
+ No generic substring deletion of “owner” is appropriate: existing transport
122
+ resource ownership and admission responsibilities still have their own meaning.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oxianjs",
3
- "version": "0.21.3",
4
- "description": "Oxian owners and the in-process host",
3
+ "version": "0.22.0-next.29ff900",
4
+ "description": "Fetch-native applications and streaming worker dispatch",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -12,17 +12,52 @@
12
12
  "node": ">=24"
13
13
  },
14
14
  "exports": {
15
- "./host": {
16
- "types": "./src/host/index.d.ts",
17
- "default": "./src/host/index.js"
15
+ ".": {
16
+ "types": "./src/mod.d.ts",
17
+ "default": "./src/mod.js"
18
18
  },
19
- "./owner": {
20
- "types": "./src/owner/index.d.ts",
21
- "default": "./src/owner/index.js"
19
+ "./adapters/node": {
20
+ "types": "./src/adapters/node/index.d.ts",
21
+ "default": "./src/adapters/node/index.js"
22
+ },
23
+ "./http": {
24
+ "types": "./src/http/index.d.ts",
25
+ "default": "./src/http/index.js"
26
+ },
27
+ "./hypervisor": {
28
+ "types": "./src/hypervisor/index.d.ts",
29
+ "default": "./src/hypervisor/index.js"
30
+ },
31
+ "./protocol": {
32
+ "types": "./src/protocol/index.d.ts",
33
+ "default": "./src/protocol/index.js"
34
+ },
35
+ "./providers": {
36
+ "types": "./src/providers/core.d.ts",
37
+ "default": "./src/providers/core.js"
38
+ },
39
+ "./transport": {
40
+ "types": "./src/transport/index.d.ts",
41
+ "default": "./src/transport/index.js"
42
+ },
43
+ "./work": {
44
+ "types": "./src/work/index.d.ts",
45
+ "default": "./src/work/index.js"
46
+ },
47
+ "./worker": {
48
+ "types": "./src/worker/index.d.ts",
49
+ "default": "./src/worker/index.js"
22
50
  }
23
51
  },
52
+ "dependencies": {
53
+ "ws": "8.22.0"
54
+ },
24
55
  "files": [
25
56
  "src",
26
- "LICENSE"
57
+ "docs/owner-retirement.md",
58
+ "docs/migration-0.22.md",
59
+ "CHANGELOG.md",
60
+ "LICENSE",
61
+ "README.md"
27
62
  ]
28
63
  }
@@ -0,0 +1,17 @@
1
+ /** Maximum accepted WebSocket connections in a listener's budget. */
2
+ export declare const CONNECTION_BUDGET = 1000;
3
+ /** One accepted connection. Releasing it twice is harmless. */
4
+ export type ConnectionLease = Readonly<{
5
+ release(): void;
6
+ }>;
7
+ /** May be shared by several listeners in the same process. */
8
+ export type ConnectionBudget = Readonly<{
9
+ connect(): ConnectionLease;
10
+ snapshot(): Readonly<{
11
+ connections: number;
12
+ budget: number;
13
+ }>;
14
+ /** Refuses new connections; existing leases can still be released. */
15
+ close(): void;
16
+ }>;
17
+ export declare function createConnectionBudget(maxConnections?: number): ConnectionBudget;
@@ -0,0 +1,34 @@
1
+ /** Maximum accepted WebSocket connections in a listener's budget. */
2
+ export const CONNECTION_BUDGET = 1_000;
3
+ export function createConnectionBudget(maxConnections = CONNECTION_BUDGET) {
4
+ if (!Number.isSafeInteger(maxConnections) ||
5
+ maxConnections < 1 ||
6
+ maxConnections > CONNECTION_BUDGET) {
7
+ throw new RangeError(`maxConnections must be from 1 to ${CONNECTION_BUDGET}`);
8
+ }
9
+ let connections = 0;
10
+ let closed = false;
11
+ return Object.freeze({
12
+ connect() {
13
+ if (closed)
14
+ throw new Error("connection budget is closed");
15
+ if (connections >= maxConnections) {
16
+ throw new Error("connection budget exceeded");
17
+ }
18
+ connections += 1;
19
+ let released = false;
20
+ return Object.freeze({
21
+ release() {
22
+ if (released)
23
+ return;
24
+ released = true;
25
+ connections -= 1;
26
+ },
27
+ });
28
+ },
29
+ snapshot: () => Object.freeze({ connections, budget: maxConnections }),
30
+ close() {
31
+ closed = true;
32
+ },
33
+ });
34
+ }
@@ -0,0 +1,4 @@
1
+ export { serve } from "./server.ts";
2
+ export type { NodeServeOptions } from "./types.ts";
3
+ export { CONNECTION_BUDGET, createConnectionBudget, } from "./connection-budget.ts";
4
+ export type { ConnectionBudget, ConnectionLease } from "./connection-budget.ts";
@@ -0,0 +1,2 @@
1
+ export { serve } from "./server.js";
2
+ export { CONNECTION_BUDGET, createConnectionBudget, } from "./connection-budget.js";
@@ -0,0 +1,4 @@
1
+ import type { HypervisorListener } from "../../hypervisor/types.ts";
2
+ import type { NodeServeOptions } from "./types.ts";
3
+ /** Starts a Node listener for an existing application-owned Hypervisor. */
4
+ export declare function serve(options: NodeServeOptions): Promise<HypervisorListener>;
@@ -0,0 +1,178 @@
1
+ import { createServer, } from "node:http";
2
+ import { Readable } from "node:stream";
3
+ import { pipeline } from "node:stream/promises";
4
+ import { WebSocketServer } from "ws";
5
+ import { createConnectionBudget, } from "./connection-budget.js";
6
+ import { observeListenerSettlement } from "../../hypervisor/internal/listener.js";
7
+ import { createHypervisorError, listenerUrl, } from "../../hypervisor/internal/primitives.js";
8
+ import { WORKER_PROTOCOL_LIMITS } from "../../protocol/limits.js";
9
+ import { adaptWebSocket } from "../../transport/socket.js";
10
+ /** Starts a Node listener for an existing application-owned Hypervisor. */
11
+ export async function serve(options) {
12
+ const { hypervisor, hostname = "127.0.0.1", port = 0, signal } = options;
13
+ const budget = options.budget ?? createConnectionBudget();
14
+ if (!hypervisor.snapshot().acceptingConnections) {
15
+ throw createHypervisorError("shutting_down", "cannot listen after Hypervisor shutdown begins");
16
+ }
17
+ signal?.throwIfAborted();
18
+ const selected = new WeakMap();
19
+ const sockets = new WebSocketServer({
20
+ noServer: true,
21
+ perMessageDeflate: false,
22
+ maxPayload: WORKER_PROTOCOL_LIMITS.maxDataPayloadBytes +
23
+ WORKER_PROTOCOL_LIMITS.maxControlFrameBytes,
24
+ handleProtocols: (offered, request) => {
25
+ const protocol = selected.get(request);
26
+ return protocol !== undefined && offered.has(protocol) ? protocol : false;
27
+ },
28
+ });
29
+ let origin = listenerUrl(hostname, port);
30
+ const server = createServer((request, response) => {
31
+ void respond(request, response);
32
+ });
33
+ async function respond(request, response) {
34
+ try {
35
+ const decision = hypervisor.prepare(toRequest(request, origin, true));
36
+ if (decision.kind === "upgrade") {
37
+ decision.cancel("upgrade_event_required");
38
+ await send(response, new Response("WebSocket upgrade required", { status: 400 }));
39
+ return;
40
+ }
41
+ await send(response, await decision.response);
42
+ }
43
+ catch {
44
+ if (!response.headersSent)
45
+ response.writeHead(500);
46
+ response.end();
47
+ }
48
+ }
49
+ server.on("upgrade", (request, socket, head) => {
50
+ void upgrade(request, socket, head);
51
+ });
52
+ async function upgrade(request, socket, head) {
53
+ if (takeConnection(budget, socket) === "refused") {
54
+ await reject(socket, new Response("connection budget exceeded", { status: 503 }));
55
+ return;
56
+ }
57
+ let cancel;
58
+ const failed = () => cancel?.("upgrade_closed");
59
+ socket.on("error", failed);
60
+ socket.once("close", failed);
61
+ try {
62
+ const decision = hypervisor.prepare(toRequest(request, origin, false));
63
+ if (decision.kind === "response") {
64
+ await reject(socket, await decision.response);
65
+ return;
66
+ }
67
+ cancel = decision.cancel;
68
+ selected.set(request, decision.protocol);
69
+ sockets.handleUpgrade(request, socket, head, (connection) => {
70
+ socket.removeListener("error", failed);
71
+ socket.removeListener("close", failed);
72
+ cancel = undefined;
73
+ try {
74
+ decision.attach(adaptWebSocket(connection), decision.protocol);
75
+ }
76
+ catch {
77
+ decision.cancel("connection_rejected");
78
+ connection.close(4400, "connection_rejected");
79
+ }
80
+ });
81
+ }
82
+ catch {
83
+ cancel?.("invalid_upgrade");
84
+ socket.destroy();
85
+ }
86
+ }
87
+ sockets.on("wsClientError", (_error, socket) => {
88
+ socket.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n");
89
+ });
90
+ const finished = Promise.withResolvers();
91
+ server.on("error", (error) => finished.reject(error));
92
+ server.once("close", () => finished.resolve());
93
+ await new Promise((resolve, reject) => {
94
+ server.once("error", reject);
95
+ server.listen(port, hostname, () => {
96
+ server.removeListener("error", reject);
97
+ resolve();
98
+ });
99
+ });
100
+ const address = server.address();
101
+ if (address === null || typeof address === "string") {
102
+ throw new TypeError("Hypervisor listener must use a TCP address");
103
+ }
104
+ origin = listenerUrl(address.address, address.port);
105
+ let closed;
106
+ const shutdown = () => {
107
+ if (closed !== undefined)
108
+ return closed;
109
+ for (const client of sockets.clients)
110
+ client.terminate();
111
+ sockets.close();
112
+ server.close();
113
+ server.closeAllConnections();
114
+ closed = finished.promise.catch(() => undefined);
115
+ return closed;
116
+ };
117
+ const listener = Object.freeze({
118
+ hostname: address.address,
119
+ port: address.port,
120
+ url: origin,
121
+ finished: finished.promise,
122
+ shutdown,
123
+ });
124
+ observeListenerSettlement({
125
+ finished: finished.promise,
126
+ signal,
127
+ close: shutdown,
128
+ cleanup: () => undefined,
129
+ });
130
+ return listener;
131
+ }
132
+ function takeConnection(budget, socket) {
133
+ if (budget === undefined)
134
+ return undefined;
135
+ let held;
136
+ try {
137
+ held = budget.connect();
138
+ }
139
+ catch {
140
+ return "refused";
141
+ }
142
+ if (socket.destroyed)
143
+ held.release();
144
+ else
145
+ socket.once("close", () => held.release());
146
+ return undefined;
147
+ }
148
+ function toRequest(request, origin, withBody) {
149
+ const headers = new Headers();
150
+ for (let index = 0; index < request.rawHeaders.length; index += 2) {
151
+ headers.append(request.rawHeaders[index], request.rawHeaders[index + 1]);
152
+ }
153
+ const hasBody = withBody && request.method !== "GET" && request.method !== "HEAD";
154
+ return new Request(new URL(request.url ?? "/", origin), {
155
+ method: request.method,
156
+ headers,
157
+ ...(hasBody
158
+ ? {
159
+ body: Readable.toWeb(request),
160
+ duplex: "half",
161
+ }
162
+ : {}),
163
+ });
164
+ }
165
+ async function send(target, response) {
166
+ target.writeHead(response.status, [...response.headers.entries()].flat());
167
+ if (response.body === null) {
168
+ target.end();
169
+ return;
170
+ }
171
+ await pipeline(Readable.fromWeb(response.body), target);
172
+ }
173
+ async function reject(socket, response) {
174
+ const body = Buffer.from(await response.arrayBuffer());
175
+ socket.write(`HTTP/1.1 ${response.status} ${response.statusText || "Rejected"}\r\n` +
176
+ `Connection: close\r\nContent-Length: ${body.length}\r\n\r\n`);
177
+ socket.end(body);
178
+ }
@@ -0,0 +1,7 @@
1
+ import type { ConnectionBudget } from "./connection-budget.ts";
2
+ import type { Hypervisor, HypervisorListenOptions } from "../../hypervisor/types.ts";
3
+ export type NodeServeOptions = Readonly<{
4
+ hypervisor: Hypervisor;
5
+ /** Each accepted upgrade takes one connection from this shared budget. */
6
+ budget?: ConnectionBudget;
7
+ }> & HypervisorListenOptions;
@@ -0,0 +1,6 @@
1
+ import type { Application, ApplicationOptions } from "./types.ts";
2
+ /**
3
+ * Initializes one Fetch-native application instance. Route modules have
4
+ * already been imported and validated by the file-router factory.
5
+ */
6
+ export declare function createApplication<State = undefined>(options: ApplicationOptions<State>): Promise<Application<State>>;