@objectstack/types 17.2.0 → 17.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1106 -0
- package/dist/index.d.mts +649 -11
- package/dist/index.d.ts +649 -11
- package/dist/index.js +331 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +313 -3
- package/dist/index.mjs.map +1 -1
- package/dist/node.d.mts +12 -3
- package/dist/node.d.ts +12 -3
- package/dist/node.js +199 -2
- package/dist/node.js.map +1 -1
- package/dist/node.mjs +201 -4
- package/dist/node.mjs.map +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,1111 @@
|
|
|
1
1
|
# @objectstack/types
|
|
2
2
|
|
|
3
|
+
## 17.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3d3f60e: An approval decision that lands while its flow run strands now says so in fields, not only in prose.
|
|
8
|
+
|
|
9
|
+
`POST /api/v1/approvals/requests/{id}/reject` — and its sibling decision doors — could produce three coexisting outcomes from one call: the caller read HTTP 500, the request row **was** in its terminal status and had left the pending inbox, and the workflow run was stranded. A caller reading 500 has one honest inference available — "the rejection did not happen" — and it was the wrong one, so scripts and operators retried or escalated against a decision that was already durable. The only carrier of the truth was English prose in `error`, so finding the affected run meant regexing a run id out of a sentence, and nothing said whether that run could be repaired at all.
|
|
10
|
+
|
|
11
|
+
The 500 stays. A recorded decision whose flow never advances is still a failure and is still reported as one; the door does not become atomic and no decision is ever rolled back. What changed is that it stops discarding what the engine already said:
|
|
12
|
+
|
|
13
|
+
- **The `RESUME_FAILED` body gains four fields**, additively — `finalized` (always `true`: the decision stands), `decision`, `runId`, and `repairable`. Existing consumers see the same `code`, the same `error` and the same status.
|
|
14
|
+
- **`repairable` carries the engine's own discriminator** — `AutomationResult.status === 'stranded'`, the state stamped on exactly the exit that journals a repair snapshot. `false` is the answer for every other failure, including a lost run: absence of the signal is not repairability, and a repair verb that would refuse is worse than no promise.
|
|
15
|
+
- **`serviceResume` carries `status`** through to the door. It previously read only `success` / `code` / `error`, and the stranded exit reports a `status` and no `code` at all — so the platform's own repairability signal died one line before the envelope was built.
|
|
16
|
+
|
|
17
|
+
`@objectstack/types` gains `strandedDecisionFailure` / `strandedDecisionDetails` and the `StrandedDecisionDetails` type — the constructor and its recogniser in one module, so the producing service and the REST door cannot drift. A `RESUME_FAILED` raised without that carrier answers exactly the body it always did; the door never synthesises the envelope.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 088f761: `createHostImporter` now loads the `import` build of an ALIASED dual-published package, instead of silently keeping its `require` build.
|
|
22
|
+
|
|
23
|
+
An alias declaration — `{"dependencies": {"foo": "npm:bar@1"}}` — installs a package whose manifest is named `bar` under the key `foo`. On the path where CommonJS resolution SUCCEEDS, the importer re-decides only the CONDITION (it asks the package which entry an `import()` gets, so the caller's ESM chain and this load share one instance). That re-decision recognised the package root by walking up from the resolved entry until it found a manifest named after the DECLARATION KEY — `foo` — while an aliased install's manifest is named `bar`. The walk therefore never matched, the re-decision produced nothing, and the load fell back to whatever the CommonJS resolver had answered: the `require` condition.
|
|
24
|
+
|
|
25
|
+
For an aliased dual publish that left the process holding two live copies of one package — the CommonJS build behind the host importer, the `import` build in the caller's own chain — which is exactly the split the condition re-decision exists to remove: a plugin registry, a singleton kernel, a module-level cache, one copy each.
|
|
26
|
+
|
|
27
|
+
The expectation now comes from the host's own declaration (`npm:name@range`, aliased `workspace:name@range`), the same reading the ESM-only fallback finder has used since it learned about aliases. Nothing about the check's strictness moves: an alias naming one package still does not license a directory holding another, and a non-aliased declaration is still verified against its key. Declarations that name a LOCATION rather than a package (`link:`, `file:`) carry no name to expect, so they keep today's behaviour unchanged.
|
|
28
|
+
|
|
29
|
+
Measured population for the behaviour change: zero aliased declarations exist across this workspace's 875 dependency declarations, and 867 of 867 installed declarations already match their key — no ordinary, non-aliased install reaches this path.
|
|
30
|
+
- c5d6803: Published `.js.map` files no longer embed the complete original source text (`sourcesContent`) — comments included. `sourcemap: true` was esbuild shorthand, and esbuild's own default for `sourcesContent` is `true`; nobody had decided to publish every package's full source (including `@internal`/test-only comments) to npm inside its source maps, it fell out of a default nobody had looked at. Measured before this change: 55 of 57 publishable packages shipped embedded source text, and maps were roughly half of `@objectstack/spec`'s published bytes.
|
|
31
|
+
|
|
32
|
+
`sourcesContent: false` is now set at one shared place (`scripts/tsup-drop-sources-content.mjs`, wired into every `tsup.config.ts` via tsup's `esbuildOptions` hook — most packages build through the repo-root config directly and pick this up with no config change of their own). `mappings` are untouched, so stack-trace positions still resolve correctly to the original file/line/column; only the embedded source text is gone.
|
|
33
|
+
|
|
34
|
+
`@objectstack/cli` (built with `tsc`, not `tsup`) never embedded source text to begin with — its maps' `sources` entries point at `src/**` paths that are not part of the published tarball either way. That is not a defect unique to `cli`: every `tsup`-built package's `sources` entries are `../src/**`-relative paths that are equally outside `files: ["dist", …]`, and were merely masked by the embedded content that just stopped shipping. Shipping `src/**` in `files[]` to make `sources` resolve was rejected — it would put most of the removed bytes straight back. So `cli`'s maps are left exactly as `tsc` emits them: this is now the fleet-consistent shape (accurate `mappings`, non-resolving-but-honest `sources` labels, no embedded text), not an outlier.
|
|
35
|
+
|
|
36
|
+
A new gate, `pnpm check:sourcemap-no-sources-content`, sweeps every built, non-private package's `dist/**/*.map` and fails if any of them carries a non-empty `sourcesContent` array — so a future `tsup.config.ts` that skips the shared hook, or a toolchain upgrade that changes esbuild's default back, is caught rather than silently re-publishing source text.
|
|
37
|
+
- Updated dependencies [fe0d9a4]
|
|
38
|
+
- Updated dependencies [ecd2158]
|
|
39
|
+
- Updated dependencies [f2b5e46]
|
|
40
|
+
- Updated dependencies [ed7243d]
|
|
41
|
+
- Updated dependencies [6ba0db4]
|
|
42
|
+
- Updated dependencies [625b0c3]
|
|
43
|
+
- Updated dependencies [233222e]
|
|
44
|
+
- Updated dependencies [07f40e5]
|
|
45
|
+
- Updated dependencies [ceb4877]
|
|
46
|
+
- Updated dependencies [e9fcd6b]
|
|
47
|
+
- Updated dependencies [90e7e6d]
|
|
48
|
+
- Updated dependencies [2bdabe6]
|
|
49
|
+
- Updated dependencies [ca326b5]
|
|
50
|
+
- Updated dependencies [8f404a5]
|
|
51
|
+
- Updated dependencies [68437d4]
|
|
52
|
+
- Updated dependencies [abb140c]
|
|
53
|
+
- Updated dependencies [8333a6c]
|
|
54
|
+
- Updated dependencies [3e3ecb0]
|
|
55
|
+
- Updated dependencies [3030369]
|
|
56
|
+
- Updated dependencies [d5d8d50]
|
|
57
|
+
- Updated dependencies [e08892d]
|
|
58
|
+
- Updated dependencies [ae05f2e]
|
|
59
|
+
- Updated dependencies [b548e43]
|
|
60
|
+
- Updated dependencies [c463d03]
|
|
61
|
+
- Updated dependencies [64bd6a3]
|
|
62
|
+
- Updated dependencies [13c48c2]
|
|
63
|
+
- Updated dependencies [132742f]
|
|
64
|
+
- Updated dependencies [85a2459]
|
|
65
|
+
- Updated dependencies [50dc214]
|
|
66
|
+
- Updated dependencies [e89fa92]
|
|
67
|
+
- Updated dependencies [e9fcd6b]
|
|
68
|
+
- Updated dependencies [8976ea1]
|
|
69
|
+
- Updated dependencies [56fe8c2]
|
|
70
|
+
- Updated dependencies [acabd24]
|
|
71
|
+
- Updated dependencies [ab50c8f]
|
|
72
|
+
- Updated dependencies [6491463]
|
|
73
|
+
- Updated dependencies [89cf4d6]
|
|
74
|
+
- Updated dependencies [21c5dcb]
|
|
75
|
+
- Updated dependencies [6d4d5d3]
|
|
76
|
+
- Updated dependencies [ed5d557]
|
|
77
|
+
- Updated dependencies [bca21f7]
|
|
78
|
+
- Updated dependencies [e9fcd6b]
|
|
79
|
+
- Updated dependencies [1a7a7c9]
|
|
80
|
+
- Updated dependencies [e9fcd6b]
|
|
81
|
+
- Updated dependencies [ef3a138]
|
|
82
|
+
- Updated dependencies [68d5dfd]
|
|
83
|
+
- Updated dependencies [3e21cf0]
|
|
84
|
+
- Updated dependencies [4cfc93b]
|
|
85
|
+
- Updated dependencies [efd6b43]
|
|
86
|
+
- Updated dependencies [859ded3]
|
|
87
|
+
- Updated dependencies [fa125f3]
|
|
88
|
+
- Updated dependencies [74628d9]
|
|
89
|
+
- Updated dependencies [a646120]
|
|
90
|
+
- Updated dependencies [6f1ce7d]
|
|
91
|
+
- Updated dependencies [7778115]
|
|
92
|
+
- Updated dependencies [2c753fe]
|
|
93
|
+
- Updated dependencies [52804cd]
|
|
94
|
+
- Updated dependencies [3f89967]
|
|
95
|
+
- Updated dependencies [53cf263]
|
|
96
|
+
- Updated dependencies [21aabbc]
|
|
97
|
+
- Updated dependencies [9c270bb]
|
|
98
|
+
- Updated dependencies [76c8c5a]
|
|
99
|
+
- Updated dependencies [a84e1ce]
|
|
100
|
+
- Updated dependencies [bf1054a]
|
|
101
|
+
- Updated dependencies [d8d2776]
|
|
102
|
+
- Updated dependencies [222dc0f]
|
|
103
|
+
- Updated dependencies [e9fcd6b]
|
|
104
|
+
- Updated dependencies [32c917d]
|
|
105
|
+
- Updated dependencies [f9a3c32]
|
|
106
|
+
- Updated dependencies [f502898]
|
|
107
|
+
- Updated dependencies [af7edfe]
|
|
108
|
+
- Updated dependencies [b60f48b]
|
|
109
|
+
- Updated dependencies [c78c918]
|
|
110
|
+
- Updated dependencies [cf9bda4]
|
|
111
|
+
- Updated dependencies [784cb92]
|
|
112
|
+
- Updated dependencies [7629f4d]
|
|
113
|
+
- Updated dependencies [51df9fd]
|
|
114
|
+
- Updated dependencies [a7da4de]
|
|
115
|
+
- Updated dependencies [de0bcdd]
|
|
116
|
+
- Updated dependencies [70f7d6d]
|
|
117
|
+
- Updated dependencies [c677cda]
|
|
118
|
+
- Updated dependencies [554a160]
|
|
119
|
+
- Updated dependencies [f7da71e]
|
|
120
|
+
- Updated dependencies [7f745c3]
|
|
121
|
+
- Updated dependencies [5eb24f8]
|
|
122
|
+
- Updated dependencies [2a3decc]
|
|
123
|
+
- Updated dependencies [cc00df2]
|
|
124
|
+
- Updated dependencies [f4e6adf]
|
|
125
|
+
- Updated dependencies [ee4a59b]
|
|
126
|
+
- Updated dependencies [4db3c61]
|
|
127
|
+
- Updated dependencies [5ca314a]
|
|
128
|
+
- Updated dependencies [e0af1a8]
|
|
129
|
+
- Updated dependencies [414c1fc]
|
|
130
|
+
- Updated dependencies [22c0279]
|
|
131
|
+
- Updated dependencies [0db2947]
|
|
132
|
+
- Updated dependencies [92b5d7f]
|
|
133
|
+
- Updated dependencies [613bfbd]
|
|
134
|
+
- Updated dependencies [abae16a]
|
|
135
|
+
- Updated dependencies [094b8fd]
|
|
136
|
+
- Updated dependencies [c7aca0d]
|
|
137
|
+
- Updated dependencies [c1d8f98]
|
|
138
|
+
- Updated dependencies [8e0b297]
|
|
139
|
+
- Updated dependencies [5f7fa1d]
|
|
140
|
+
- Updated dependencies [87f0ccc]
|
|
141
|
+
- Updated dependencies [aedbaef]
|
|
142
|
+
- Updated dependencies [c5d6803]
|
|
143
|
+
- Updated dependencies [10d05bb]
|
|
144
|
+
- Updated dependencies [69602e5]
|
|
145
|
+
- Updated dependencies [c3ce76c]
|
|
146
|
+
- Updated dependencies [7936b29]
|
|
147
|
+
- Updated dependencies [46803fa]
|
|
148
|
+
- Updated dependencies [c2a336c]
|
|
149
|
+
- Updated dependencies [9f890d3]
|
|
150
|
+
- Updated dependencies [0bb2318]
|
|
151
|
+
- Updated dependencies [f7db8f4]
|
|
152
|
+
- Updated dependencies [1ecee3e]
|
|
153
|
+
- Updated dependencies [9408b7f]
|
|
154
|
+
- Updated dependencies [e9fcd6b]
|
|
155
|
+
- Updated dependencies [9bcd9be]
|
|
156
|
+
- Updated dependencies [b398ad2]
|
|
157
|
+
- Updated dependencies [99261a7]
|
|
158
|
+
- Updated dependencies [81b426f]
|
|
159
|
+
- Updated dependencies [001af1c]
|
|
160
|
+
- Updated dependencies [fb77aa5]
|
|
161
|
+
- Updated dependencies [581d8f8]
|
|
162
|
+
- Updated dependencies [f81afe3]
|
|
163
|
+
- Updated dependencies [40a44b9]
|
|
164
|
+
- Updated dependencies [7a7fb03]
|
|
165
|
+
- Updated dependencies [8fd246d]
|
|
166
|
+
- @objectstack/spec@17.4.0
|
|
167
|
+
|
|
168
|
+
## 17.3.0
|
|
169
|
+
|
|
170
|
+
### Minor Changes
|
|
171
|
+
|
|
172
|
+
- 6a180e4: fix(core,rest,services)!: a permission-store read failure now fails LOUD instead of resolving as an authenticated caller holding zero capabilities (#13279)
|
|
173
|
+
|
|
174
|
+
**BREAKING** runtime behaviour change on the shared authorization resolver,
|
|
175
|
+
shipped as `minor` under the repo's launch-window convention.
|
|
176
|
+
|
|
177
|
+
`resolveAuthzContext`'s per-read helper `tryFind` answered a THROWN read exactly
|
|
178
|
+
the way it answered an EMPTY one: `[]`. So an outage of the permission store
|
|
179
|
+
resolved as a well-formed context for an authenticated principal holding no
|
|
180
|
+
capabilities, and the package-management door answered
|
|
181
|
+
`403 FORBIDDEN` — "Reading packages requires the `studio.access` or
|
|
182
|
+
`setup.access` capability." That answer was measured byte-identical
|
|
183
|
+
(`JSON.stringify` equal, against a control that separates two answers which do
|
|
184
|
+
differ) to what a caller who genuinely holds nothing receives. An administrator
|
|
185
|
+
was told they lack a capability, during an outage of the store that holds the
|
|
186
|
+
capability.
|
|
187
|
+
|
|
188
|
+
Maintainer ruling 2026-08-30, verbatim 「第一批其余同意」: `tryFind` 区分「无行」
|
|
189
|
+
与「读失败」,读失败 fail-loud —— 权限库不可达时不再解析为「已认证零能力」,而是
|
|
190
|
+
响亮拒绝(与真实能力拒绝的 403 可区分)。
|
|
191
|
+
|
|
192
|
+
Second maintainer ruling the same day (第 5 场总监席决裁批 #9, verbatim 「同意」),
|
|
193
|
+
after implementing the first one showed that "the read failed" is two facts:
|
|
194
|
+
采**选项 A** —— 把 `isMissingTableError` 从 `@objectstack/metadata` 迁至
|
|
195
|
+
`@objectstack/types`(core 已依赖),metadata 保留 re-export 兼容;`tryFind` 仅对
|
|
196
|
+
**未被判定为「表未 provision」**的读失败抛 `AuthzStoreUnavailableError`。
|
|
197
|
+
|
|
198
|
+
**What changed.** A permission-store read that is issued and throws now raises
|
|
199
|
+
`AuthzStoreUnavailableError`, which carries the EXISTING ADR-0112 wire code
|
|
200
|
+
`SERVICE_UNAVAILABLE` and status `503`. No code is added to the closed wire
|
|
201
|
+
vocabulary and no response envelope gains or loses a key — only which declared
|
|
202
|
+
code an outage selects. Doors that map thrown errors through
|
|
203
|
+
`resolveThrownHttpError` answer 503 with no per-door change.
|
|
204
|
+
|
|
205
|
+
**What did NOT change**, and is pinned:
|
|
206
|
+
|
|
207
|
+
- A reachable, genuinely EMPTY store (reads return no rows) still resolves to
|
|
208
|
+
zero capabilities.
|
|
209
|
+
- A genuine capability denial still answers `403 FORBIDDEN` with its message.
|
|
210
|
+
- An ABSENT engine (`ql` unwired, so no read is ever issued) still resolves to
|
|
211
|
+
an empty-but-valid envelope.
|
|
212
|
+
- Anonymous requests never reach the store, so an outage cannot make them loud.
|
|
213
|
+
- A REAL engine whose `sys_*` tables were never provisioned resolves to zero
|
|
214
|
+
capabilities, quietly — pinned to be byte-identical to the empty-store
|
|
215
|
+
envelope, in every dialect spelling and in the production wrapper shape where
|
|
216
|
+
the driver's phrase is on `cause` rather than the outer message.
|
|
217
|
+
|
|
218
|
+
**The boundary between the two kinds of read failure.** An earlier revision of
|
|
219
|
+
this changeset claimed "embedders without a data plane are unaffected". That
|
|
220
|
+
claim was too broad; it is retracted here, and the gap it named is now closed
|
|
221
|
+
rather than merely disclosed. A read also throws when the table was never
|
|
222
|
+
PROVISIONED — a real engine, wired and reachable, whose `sys_*` tables were
|
|
223
|
+
never created — and that is a supported deployment shape, not an outage. There
|
|
224
|
+
"zero capabilities" is the TRUE answer rather than a fabrication: nothing is
|
|
225
|
+
provisioned, so nothing was withheld. Only an UNREACHABLE store — the ruling's
|
|
226
|
+
own word 不可达 — leaves the capability set unknown, and only an unknown answer
|
|
227
|
+
may not be reported as a denial.
|
|
228
|
+
|
|
229
|
+
Treating the two alike was measured, not theorised: it turned four CI suites
|
|
230
|
+
red, all from `no such table` on `sys_user` / `sys_member` /
|
|
231
|
+
`sys_user_position` / `sys_user_permission_set`. Ordinary CRUD in
|
|
232
|
+
`@objectstack/client` answered `503`; batch validation errors that owe `400`
|
|
233
|
+
answered `503`, because authorization refused before validation ran; runtime
|
|
234
|
+
notifications answered `401` where authenticated callers must be served `200`;
|
|
235
|
+
and two `.integration.test.ts` noise guards reported that the driver and engine
|
|
236
|
+
diagnostics for `sys_position` stopped being emitted — the eager throw aborted
|
|
237
|
+
the resolution before that later read was ever issued, so a change made to stop
|
|
238
|
+
a failed read being silent had made two other channels silent.
|
|
239
|
+
|
|
240
|
+
`tryFind` therefore raises `AuthzStoreUnavailableError` only for a read failure
|
|
241
|
+
that is NOT positively identified as an unprovisioned table.
|
|
242
|
+
|
|
243
|
+
**`isMissingTableError` moved to `@objectstack/types`.** The classifier that
|
|
244
|
+
draws that boundary already existed and was already right — driver-code based
|
|
245
|
+
rather than prose-sniffing, documented so that "cannot say" never means "be
|
|
246
|
+
loud". It lived in `@objectstack/metadata`, which DEPENDS ON `@objectstack/core`,
|
|
247
|
+
so the resolver could not import it. Rather than keep a second copy of a
|
|
248
|
+
security-relevant predicate, the ruling relocated the one classifier to
|
|
249
|
+
`@objectstack/types` — the package core already depends on, and the repo's own
|
|
250
|
+
stated Home rule for a cross-package error predicate ("every consumer of the
|
|
251
|
+
question already depends on it, so adopting the predicate never adds an edge",
|
|
252
|
+
`packages/types/src/unique-violation.ts`). `@objectstack/metadata/errors` still
|
|
253
|
+
exports `isMissingTableError`, re-exported from the new home, so no consumer of
|
|
254
|
+
that published subpath changes.
|
|
255
|
+
|
|
256
|
+
Its sibling `isSchemaAlreadyExistsError` moved with it — the two are not two
|
|
257
|
+
modules but two signatures over one matcher, and separating them would have
|
|
258
|
+
meant re-rolling the matcher, which is the duplication the module exists to
|
|
259
|
+
prevent. Both are now exported from `@objectstack/types`; the metadata subpath
|
|
260
|
+
deliberately still publishes only `isMissingTableError`, which is the only one
|
|
261
|
+
anything imports through it.
|
|
262
|
+
|
|
263
|
+
⚠️ **Signed-off risk, recorded because it is load-bearing.** Gating loudness on
|
|
264
|
+
a driver-error predicate was approved with its false-positive direction stated:
|
|
265
|
+
mis-reading a genuine outage as "table not provisioned" silently restores the
|
|
266
|
+
quiet 403 this change removes, with no thrown error and no other failing test.
|
|
267
|
+
That direction is accepted, not overlooked — the predicate keys on driver codes,
|
|
268
|
+
SQLSTATEs and errnos first, excludes the known superstring traps up front, and
|
|
269
|
+
returns `false` for anything it does not positively recognise, so an
|
|
270
|
+
unrecognised outage stays loud by default. The risk is written beside the
|
|
271
|
+
predicate in `resolve-authz-context.ts` and both directions are pinned by name
|
|
272
|
+
in `authz-store-unavailable.test.ts`. ⛔ Do not widen `isMissingTableError` to
|
|
273
|
+
make a first boot quieter: every widening moves outages into the quiet branch.
|
|
274
|
+
|
|
275
|
+
**All-transport, not just REST.** Every transport authorizing through
|
|
276
|
+
`resolveAuthzContext` inherits this. Six of the eight production transports
|
|
277
|
+
wrapped the call in a fail-closed `catch` that would have re-silenced the
|
|
278
|
+
outage — measured, not assumed: with the resolver loud but the nets untouched,
|
|
279
|
+
the package door answered `401`, i.e. the outage merely changed disguises. Those
|
|
280
|
+
`catch` blocks now re-raise via `isAuthzStoreUnavailableError` and keep their
|
|
281
|
+
previous behaviour for every other fault. The transport set is rebuilt from
|
|
282
|
+
source and audited for set equality on every test run, so a transport added
|
|
283
|
+
later cannot inherit the old silence unnoticed.
|
|
284
|
+
|
|
285
|
+
Callers that treat any throw from `resolveAuthzContext` as "anonymous" should
|
|
286
|
+
re-raise `isAuthzStoreUnavailableError(err)` instead: degrading it restores the
|
|
287
|
+
disguise this removes.
|
|
288
|
+
|
|
289
|
+
<!-- adr-0087: not-required (runtime-interface-only packages/core/src/security/resolve-authz-context.ts#ResolvedAuthzContext, packages/core/src/security/authz-store-unavailable.ts#AuthzStoreUnavailableError) The breaking surface is runtime TypeScript in `@objectstack/core`'s security module and nothing else: `resolveAuthzContext` stops always-resolving and raises `AuthzStoreUnavailableError` when a permission-store read is issued and throws. NO metadata surface is touched in either direction. No Zod schema changes, no `packages/spec` declaration is added or removed, no authorable key moves, no stored row shape changes, and no object definition is edited — a customer's metadata app is byte-for-byte unaffected, so `objectstack migrate meta` has nothing to visit and there is no tombstone to mint. The wire vocabulary is likewise untouched: `SERVICE_UNAVAILABLE` is an EXISTING `StandardErrorCode` member that `HttpStatusErrorCodeMap` already maps to 503, so this change only selects a different DECLARED code for an outage rather than adding one. Both named symbols resolve at HEAD as exported declarations whose files are not `*.zod.ts`, are not under `packages/spec/src/contracts/`, are not object definitions and are not `z.input` projections; neither is referenced in code by any metadata surface (the `packages/spec` hits for `resolveAuthzContext` are comment prose describing the envelope, which this gate masks). The channel that reaches an affected consumer is therefore code review and this changeset, never the upgrade guide: a ledger entry could not express "your fail-closed catch should re-raise this error", because there is no metadata for a migration to rewrite. -->
|
|
290
|
+
- 0fb3044: fix(types): load a declared ESM-only host package through `createHostImporter`, and split its failure kind (#14041)
|
|
291
|
+
|
|
292
|
+
The declared leg's finder is `hostRequire.resolve(pkg)` — a **CommonJS**
|
|
293
|
+
resolution. A host-app package publishing only an `import` condition
|
|
294
|
+
(`{"exports": {".": {"import": "./dist/index.js"}}}`, ordinary for pure-ESM
|
|
295
|
+
publishes outside this workspace) made that resolve throw
|
|
296
|
+
`ERR_PACKAGE_PATH_NOT_EXPORTED`, and the leg classified **every** resolver
|
|
297
|
+
throw as `declared-unresolvable`: the load hard-failed, worded as an INSTALL
|
|
298
|
+
problem — `pnpm install`, un-prune, rebuild — about an install that was fine.
|
|
299
|
+
Nothing the message prescribed could help.
|
|
300
|
+
|
|
301
|
+
**The finder.** When — and only when — `hostRequire.resolve` throws, the leg
|
|
302
|
+
now consults exactly one directory: `<hostRoot>/node_modules/<name>` (name
|
|
303
|
+
verified against the package's own manifest, then realpath'd, so its
|
|
304
|
+
transitive imports resolve from its real location exactly as on the succeeding
|
|
305
|
+
path). If that package's `exports` names an existing `import`-condition target
|
|
306
|
+
for the requested subpath, it is imported. The fallback is **strictly
|
|
307
|
+
tighter** than the CJS resolution it backs up — no `NODE_PATH`, no walk above
|
|
308
|
+
`hostRoot`, no bare `require`, and Node's invalid-segment refusal mirrored
|
|
309
|
+
before exports resolution (a subpath carrying `''`, `.`, `..` or
|
|
310
|
+
`node_modules` segments is refused exactly as both of Node's resolvers refuse
|
|
311
|
+
it, so a pattern key can never substitute a traversal span into its target) —
|
|
312
|
+
so it cannot reopen the #4719 declaration-gate hole and cannot bypass the
|
|
313
|
+
package encapsulation Node's resolvers enforce: a package reachable only
|
|
314
|
+
through a hoisted store or a parent directory stays refused, even though CJS
|
|
315
|
+
resolution can see it there, and a traversal specifier keeps the hard failure
|
|
316
|
+
it has today. And because the fallback runs only inside a catch that was a
|
|
317
|
+
hard failure before, no currently-succeeding load changes behaviour.
|
|
318
|
+
|
|
319
|
+
**The split.** When the fallback cannot help either, the failure kind is
|
|
320
|
+
decided by whether any install action could: a package absent from the host's
|
|
321
|
+
`node_modules`, or one whose manifest names a runtime target whose file is
|
|
322
|
+
missing (a dist never built, a partial publish), keeps `declared-unresolvable`
|
|
323
|
+
and the existing INSTALL wording — it is right for both. A package that is
|
|
324
|
+
installed and whose manifest names **no** runtime entry for the subpath under
|
|
325
|
+
either the `require` or the `import` conditions (a `types`-only or
|
|
326
|
+
`browser`-only publish, an unexported subpath) now fails as the new
|
|
327
|
+
`HostImportFailureKind` value **`declared-no-loadable-entry`**, with a message
|
|
328
|
+
about the package's own published shape — the remedy lives in the package,
|
|
329
|
+
and an operator is no longer sent to re-run `pnpm install` against a correct
|
|
330
|
+
install. The new error still carries `code: 'MODULE_NOT_FOUND'`, so every
|
|
331
|
+
existing caller's missing-vs-crashed classification is unchanged, and an
|
|
332
|
+
evaluation crash still propagates untouched with no kind.
|
|
333
|
+
|
|
334
|
+
Consumers that branch on `HostImportFailureKind` should add an arm for
|
|
335
|
+
`declared-no-loadable-entry`: a two-way branch written against the old
|
|
336
|
+
two-member union will fall into its else leg for the new kind, whose wording
|
|
337
|
+
("declare it") is wrong for a package that is declared and installed.
|
|
338
|
+
- db8c288: fix(types): let `sendError`'s `extra` carry `declaredCode`, so a nested-envelope route can emit the ADR-0112 open channel (#11719)
|
|
339
|
+
|
|
340
|
+
`ApiErrorSchema` has declared `declaredCode` since #9106 — the open,
|
|
341
|
+
author-authored channel that carries a metadata app's own `.code` verbatim when
|
|
342
|
+
the spelling is not a member of the closed `code` vocabulary. ADR-0112's
|
|
343
|
+
2026-08-17 amendment rules that demote **platform-wide**, and #9232 extended it
|
|
344
|
+
to the flat `/data` door, which emits the pair today.
|
|
345
|
+
|
|
346
|
+
The shared nested-envelope writer could not. `sendError`'s `extra` was typed
|
|
347
|
+
`Pick<ApiError, 'category' | 'httpStatus' | 'details' | 'requestId'>`, so
|
|
348
|
+
passing a demoted spelling was a **compile error** and every route answering the
|
|
349
|
+
nested envelope dropped it. Nothing invalid shipped — the closed `code` still
|
|
350
|
+
carried the member derived from the status — which is exactly what made the loss
|
|
351
|
+
silent and one-directional: the author's spelling gone, and a consumer told by
|
|
352
|
+
the ADR to read `declaredCode` finding nothing there. Declared-but-unemittable
|
|
353
|
+
is a `declared = enforced` gap, closed here at the one writer rather than per
|
|
354
|
+
module.
|
|
355
|
+
|
|
356
|
+
Additive: `declaredCode` joins the `Pick`. No existing call site changes, no
|
|
357
|
+
wire byte moves for any body already being emitted, and the contract's accept
|
|
358
|
+
set is untouched — the schema has always permitted the field.
|
|
359
|
+
|
|
360
|
+
⛔ Presence still MEANS demotion, and the writer does not re-derive that. The
|
|
361
|
+
caller passes `demotedDeclaredCode(thrown)` (`@objectstack/types`), exactly as
|
|
362
|
+
the flat door's `thrownCodeFields` does; that helper answers `undefined` when
|
|
363
|
+
the producer's spelling is already the vocabulary member sitting in `code`, so a
|
|
364
|
+
registered refusal never carries two spellings of one fact. Vocabulary and
|
|
365
|
+
position stay two decisions (#9232).
|
|
366
|
+
|
|
367
|
+
Pinned in `response-envelope.test.ts` by driving the real pipeline — a
|
|
368
|
+
sandbox-shaped throw carrying a tenant-authored `.code` through
|
|
369
|
+
`resolveThrownHttpError` and `demotedDeclaredCode` — and by parsing the emitted
|
|
370
|
+
body with the real `ApiErrorSchema`, asserting the field is still on it *after*
|
|
371
|
+
the parse. `ApiErrorSchema` is a plain `z.object` that strips undeclared keys,
|
|
372
|
+
so a `.success` assertion alone would have passed against a schema declaring
|
|
373
|
+
nothing.
|
|
374
|
+
- 0e5fe7f: fix(types): let `sendError`'s `extra` carry `userMessage`, so a nested-envelope route can emit the #9934 user-facing channel (#12404)
|
|
375
|
+
|
|
376
|
+
`ApiErrorSchema` declares `userMessage` — the producer-side opt-in for "this
|
|
377
|
+
exact text is addressed to the END USER" (#9934; maintainer ruling 2026-08-19 on
|
|
378
|
+
objectui#5210, option 1), where **presence IS the marking** and a consumer that
|
|
379
|
+
sees the field renders it verbatim instead of substituting its generic string
|
|
380
|
+
(#3821 preserved by construction, for everything unmarked).
|
|
381
|
+
|
|
382
|
+
Two of the three doors already emit it: the flat `/data` door through
|
|
383
|
+
`withDeclaredUserMessage` (`@objectstack/rest`) and the dispatcher door through
|
|
384
|
+
`thrown.userMessage` (`@objectstack/runtime`). The shared nested-envelope writer
|
|
385
|
+
could not — `sendError`'s `extra` was typed
|
|
386
|
+
`Pick<ApiError, 'category' | 'httpStatus' | 'details' | 'requestId' | 'declaredCode'>`,
|
|
387
|
+
so passing the field was a **compile error** and every route answering the
|
|
388
|
+
nested envelope dropped it. Nothing invalid shipped, which is what made the loss
|
|
389
|
+
silent and one-directional: the author's deliberate, localized refusal text
|
|
390
|
+
gone, and a consumer told to read `userMessage` finding nothing there.
|
|
391
|
+
Declared-but-unemittable is a `declared = enforced` gap, closed here at the one
|
|
392
|
+
writer rather than per module.
|
|
393
|
+
|
|
394
|
+
Additive: `userMessage` joins the `Pick`. No existing call site changes, no wire
|
|
395
|
+
byte moves for any body already being emitted, and the contract's accept set is
|
|
396
|
+
untouched — the schema has always declared the field.
|
|
397
|
+
|
|
398
|
+
The channel is live on both ends, which is what makes this a repair rather than
|
|
399
|
+
a new declared-but-dead surface: a hook opts in at throw time (host-side, or a
|
|
400
|
+
metadata app's sandboxed body whose `e.userMessage` crosses the QuickJS boundary
|
|
401
|
+
through `SANDBOX_ERROR_PASSTHROUGH`), and `resolveThrownHttpError` already
|
|
402
|
+
carries it onto `ThrownHttpError` for every caller of the shared resolver.
|
|
403
|
+
|
|
404
|
+
⛔ Unlike `declaredCode`, this field hands the caller **no invariant to
|
|
405
|
+
re-derive**. `declaredCode`'s presence means demotion, so its caller passes
|
|
406
|
+
`demotedDeclaredCode(thrown)`; `userMessage`'s presence means only that the
|
|
407
|
+
producer opted in, which `declaredUserMessage` has already decided (a non-empty
|
|
408
|
+
string, or nothing). The caller passes `thrown.userMessage` straight through,
|
|
409
|
+
exactly as the dispatcher door does. That difference is why `extra` stays an
|
|
410
|
+
explicit `Pick` rather than being derived from `ApiError`'s optional fields: a
|
|
411
|
+
derivation would admit every future optional on the day it lands, with nobody
|
|
412
|
+
asked what obligation the channel hands the caller — and these two fields needed
|
|
413
|
+
opposite answers to exactly that question.
|
|
414
|
+
|
|
415
|
+
Pinned in `response-envelope.test.ts` by driving the real pipeline — a hook
|
|
416
|
+
refusal shaped like the one `hook-refusal-user-facing-marking.dogfood.test.ts`
|
|
417
|
+
drives, through `resolveThrownHttpError` — and by parsing the emitted body with
|
|
418
|
+
the real `ApiErrorSchema`, asserting the field is still on it *after* the parse,
|
|
419
|
+
paired with a control showing an undeclared sibling being stripped from the same
|
|
420
|
+
body. `ApiErrorSchema` is a plain `z.object` that strips undeclared keys, so a
|
|
421
|
+
`.success` assertion alone would have passed against a schema declaring nothing.
|
|
422
|
+
A blank marking is pinned ABSENT: the writer never invents a marked message for
|
|
423
|
+
a producer that wrote none.
|
|
424
|
+
- 87ad30c: fix(types): `isMissingTableError` prefers the table a driver declared it targeted over the caller-supplied `readObject` — new `DRIVER_TARGETED_TABLE` / `declareTargetedTable` / `targetedTableOf` (#13438)
|
|
425
|
+
|
|
426
|
+
`minor` because the public entry gains three exports; the predicate's signature
|
|
427
|
+
`(error, readObject?)` is **unchanged**, and every existing caller compiles and
|
|
428
|
+
behaves as before unless the error it holds carries a declaration.
|
|
429
|
+
|
|
430
|
+
**The residual #13324 left behind.** `readObject` lets a caller say which table it
|
|
431
|
+
read, so a phrase naming a *different* relation no longer earns the benign "not
|
|
432
|
+
provisioned yet" verdict. But a caller names its **object**, and a driver compiles
|
|
433
|
+
the statement against the **physical** table — for a federated object (ADR-0015,
|
|
434
|
+
`external.remoteName`) two different names. A genuinely absent remote therefore
|
|
435
|
+
raised a phrase naming `legacy_orders` against a caller naming `crm_order`, and the
|
|
436
|
+
comparison read a real missing table as loud. The mapping lives on the driver
|
|
437
|
+
instance; no call site can fold it away.
|
|
438
|
+
|
|
439
|
+
**The channel (maintainer ruling 2026-09-01, option 2).** A driver that knows the
|
|
440
|
+
table it targeted declares it on the error it composes:
|
|
441
|
+
|
|
442
|
+
- `DRIVER_TARGETED_TABLE` — `Symbol.for('objectstack.driver.targetedTable')`, the
|
|
443
|
+
well-known key, from the global registry so a duplicated package resolves it;
|
|
444
|
+
- `declareTargetedTable(error, table)` — the producer's half: defines the name
|
|
445
|
+
**non-enumerable and non-writable** (invisible to `JSON.stringify`, `{ ...err }`,
|
|
446
|
+
`Object.keys`), first declaration wins, an empty or non-string name declares
|
|
447
|
+
nothing;
|
|
448
|
+
- `targetedTableOf(error)` — the reading half, `string | null`.
|
|
449
|
+
|
|
450
|
+
`isMissingTableError` now compares the phrase against the **declared** table at
|
|
451
|
+
any node of the `cause` chain that carries one — the nearest declaration to the
|
|
452
|
+
dialect phrase wins — and ignores the caller-supplied `readObject` from that node
|
|
453
|
+
down. Without a declaration the comparison is the #13324 one, byte-for-byte. The
|
|
454
|
+
callers stay as they are: `crm_order` is still what they pass, and they never
|
|
455
|
+
learn a federated object's remote name.
|
|
456
|
+
|
|
457
|
+
**Two consequences, both pinned.** A genuinely absent federated remote reads
|
|
458
|
+
benign again. And because a declaration is evidence the caller did not have, an
|
|
459
|
+
envelope whose phrase names a relation *other* than its declared table reads
|
|
460
|
+
**not benign even through the one-argument published form** — the #13324 verdict,
|
|
461
|
+
reached without the caller's help, in the direction the module docblock calls
|
|
462
|
+
cheap (one error line, never silent data loss). The #13324 narrowing itself does
|
|
463
|
+
not reopen: a different relation's error — a view over a dropped base, a join
|
|
464
|
+
target, a `sys_*` table hit inside the same statement — stays loud with the
|
|
465
|
+
declaration present, on every dialect fixture the existing pins carry.
|
|
466
|
+
|
|
467
|
+
`@objectstack/driver-sql` adopts the channel in the same release; the pattern is
|
|
468
|
+
one call at any future driver's envelope. `isSchemaAlreadyExistsError` is
|
|
469
|
+
untouched.
|
|
470
|
+
- 9735662: fix(security): walled postures elevate only the env-declared platform owner, never the first registrant (#11184, the framework leg of cloud#1509)
|
|
471
|
+
|
|
472
|
+
**BREAKING** for walled deployments (`OS_TENANCY_POSTURE=group` or
|
|
473
|
+
`isolated`), shipped as `minor` under the repo's launch-window convention for
|
|
474
|
+
breaking changes. Single-org deployments are byte-for-byte unchanged.
|
|
475
|
+
|
|
476
|
+
Measured defect (cloud#1509): on a walled multi-tenant SaaS with
|
|
477
|
+
`OS_TENANCY_POSTURE=isolated` and `OS_AUTH_MEMBERSHIP_POLICY=invite-only`, the
|
|
478
|
+
FIRST self-registrant received the cross-tenant `admin_full_access` grant
|
|
479
|
+
(`platform_admin`, `isPlatformAdmin: true`) and — because the default-org
|
|
480
|
+
bootstrap binds "the platform admin" — was merged into the deployment's
|
|
481
|
+
Default Organization as its owner. Whoever curls the public sign-up endpoint
|
|
482
|
+
first owned the platform.
|
|
483
|
+
|
|
484
|
+
Per the maintainer ruling of 2026-08-23 (verbatim:
|
|
485
|
+
「1509 选择 env 指定 owner 邮箱」):
|
|
486
|
+
|
|
487
|
+
- **Walled postures: platform admin comes ONLY from the env-declared owner.**
|
|
488
|
+
`bootstrapPlatformAdmin` (plugin-security) no longer promotes the oldest
|
|
489
|
+
human user when the requested posture is walled; it promotes exactly the
|
|
490
|
+
account whose email matches the new `OS_PLATFORM_OWNER_EMAIL` variable
|
|
491
|
+
(case-insensitive, matched whenever that account registers — arrival order
|
|
492
|
+
is irrelevant). Self-registrants are never promoted and, since the shared
|
|
493
|
+
`ensureDefaultOrganization` helper binds only the platform admin, are never
|
|
494
|
+
auto-merged into the Default Organization either.
|
|
495
|
+
- **Fail-closed startup refusal.** A walled posture with no
|
|
496
|
+
`OS_PLATFORM_OWNER_EMAIL` declared refuses to boot from `AuthPlugin.init()`
|
|
497
|
+
with a message naming the variable — never a silent fallback to
|
|
498
|
+
first-registrant elevation. The elevation site itself also refuses
|
|
499
|
+
(`reason: 'walled_owner_email_undeclared'`, logged at `error`) as
|
|
500
|
+
defense-in-depth for compositions that reach the bootstrap without
|
|
501
|
+
plugin-auth (`os meta resync`, bare embeddings).
|
|
502
|
+
- **Single-org posture unchanged.** "First user is owner" stays as ruled
|
|
503
|
+
reasonable there; the new variable is never consulted under `single`.
|
|
504
|
+
- The requested posture (`resolveTenancyPosture()`) is deliberately the input,
|
|
505
|
+
so a walled-requested deployment running degraded
|
|
506
|
+
(`OS_ALLOW_DEGRADED_TENANCY=1`) still refuses first-registrant elevation.
|
|
507
|
+
|
|
508
|
+
Operator action for walled deployments: set `OS_PLATFORM_OWNER_EMAIL` to the
|
|
509
|
+
operator account's email address before upgrading. Deployments that already
|
|
510
|
+
hold a human platform admin are untouched (the bootstrap remains a no-op once
|
|
511
|
+
any human holds the cross-tenant grant); the variable governs installs that
|
|
512
|
+
have not yet minted their admin. `@objectstack/types` gains the
|
|
513
|
+
`resolvePlatformOwnerEmail()` resolver and the `PLATFORM_OWNER_EMAIL_ENV`
|
|
514
|
+
constant; the verify harness declares the owner email (defaulting to its dev
|
|
515
|
+
admin) for walled fixtures.
|
|
516
|
+
|
|
517
|
+
<!-- adr-0087: not-required (no-migration-prescription) nothing authorable is removed, renamed or narrowed: no spec key, no metadata spelling and no stored row changes shape, so there is nothing for `os migrate meta` to rewrite and no ledger entry to make. The prescription above is a deployment-environment requirement (declare an env var before boot), which the ADR-0087 ledger does not carry — the refusal itself names the variable at startup. -->
|
|
518
|
+
- 4d5b4f8: feat(auth): walled deployment's declared owner is email-verified at operator-provisioned creation (#12751)
|
|
519
|
+
|
|
520
|
+
On a **walled** deployment (`OS_TENANCY_POSTURE` in the wall-enforcing
|
|
521
|
+
family), the account whose email equals the declared platform owner
|
|
522
|
+
(`OS_PLATFORM_OWNER_EMAIL`) is stamped `emailVerified` **at creation** when
|
|
523
|
+
it comes into existence through an **operator provisioning path** — extending
|
|
524
|
+
the #11343 dev-boot seeded-admin precedent to production walled boots
|
|
525
|
+
(maintainer ruling 2026-08-28, cloud#1677: 「运营方创建即视为已验证」; the
|
|
526
|
+
trust anchor is the operator's env-var declaration plus the
|
|
527
|
+
operator-executed creation, not a mailbox round-trip; SMTP stays required
|
|
528
|
+
only for inviting others).
|
|
529
|
+
|
|
530
|
+
**Which creation paths qualify** (the [#11739] audience taxonomy, not a
|
|
531
|
+
second classification):
|
|
532
|
+
|
|
533
|
+
- the **bootstrap carve-out** — the very first account on a fresh install
|
|
534
|
+
(zero human users), the one self-serve creation a walled boot admits;
|
|
535
|
+
- **admin create-user / bulk import** (`method: 'admin'`) — an act only an
|
|
536
|
+
authenticated admin session can perform;
|
|
537
|
+
- **SCIM** (`method: 'scim'`) — provisioning executed by the
|
|
538
|
+
operator-registered directory.
|
|
539
|
+
|
|
540
|
+
**Never**: non-bootstrap self-registration (including an
|
|
541
|
+
invitation-admitted registration typing the owner address), provider-class
|
|
542
|
+
JIT (the IdP asserts its own `emailVerified` at insert), any non-owner
|
|
543
|
+
address, any unwalled posture, and a later email **update** to the owner
|
|
544
|
+
address (the stamp is staged at the admission gate and consumed once by the
|
|
545
|
+
`user.create` before-hook — a seam an update cannot traverse). Dev-boot
|
|
546
|
+
behaviour (#11343) is unchanged.
|
|
547
|
+
|
|
548
|
+
The `WALLED_OWNER_NO_VERIFICATION_PATH` boot warning now probes the owner
|
|
549
|
+
account's state: a fresh walled boot with no transport and no federated
|
|
550
|
+
sign-in is **silent** (the operator's own first-account creation arrives
|
|
551
|
+
verified — the case this closes), while an owner account that already
|
|
552
|
+
exists **unverified**, a populated store whose bootstrap window is spent,
|
|
553
|
+
and an unanswerable probe keep warning. A settled deployment whose owner is
|
|
554
|
+
verified stops re-warning on every boot.
|
|
555
|
+
|
|
556
|
+
`@objectstack/types` gains `isEmailVerifiedUserRow` — the [#11343]
|
|
557
|
+
fail-closed verified-representation allow-list, moved from
|
|
558
|
+
`plugin-security`'s private copy so the elevation gate and the boot
|
|
559
|
+
diagnostic read ONE resolution (`plugin-security` now consumes it; no
|
|
560
|
+
behaviour change there).
|
|
561
|
+
|
|
562
|
+
### Patch Changes
|
|
563
|
+
|
|
564
|
+
- 101ad2c: fix(types): an aliased install (`"foo": "npm:bar@1"`) is now found by the host importer's ESM-only fallback
|
|
565
|
+
|
|
566
|
+
`createHostImporter`'s #14041 fallback finder verifies the one directory it
|
|
567
|
+
consults — `<hostRoot>/node_modules/<key>` — by matching that directory's
|
|
568
|
+
`package.json` `name` against the declared package name. An aliased install
|
|
569
|
+
fails that check by construction: `{ "dependencies": { "foo": "npm:bar@1" } }`
|
|
570
|
+
puts a manifest named `bar` at `node_modules/foo`. The finder answered
|
|
571
|
+
`absent`, and an ESM-only aliased package therefore kept the pre-#14041 INSTALL
|
|
572
|
+
wording — a confidently-wrong remedy sending an operator to run `pnpm install`
|
|
573
|
+
against an install that is already correct, on a declaration shape
|
|
574
|
+
`packageNameFromSpecifier`'s own documentation blesses.
|
|
575
|
+
|
|
576
|
+
The declaration is now parsed for the name it promises: `npm:bar@1`,
|
|
577
|
+
`npm:@acme/x@^2` and the aliased `workspace:bar@*` name the package installed
|
|
578
|
+
under the key, so that is the manifest name the finder expects there. An
|
|
579
|
+
aliased ESM-only package is rescued exactly as a plain one is, and an aliased
|
|
580
|
+
install publishing nothing loadable gets the message about the PACKAGE's own
|
|
581
|
+
shape instead of the INSTALL message.
|
|
582
|
+
|
|
583
|
+
⚠️ The manifest-name check itself is NOT loosened — that check is what keeps
|
|
584
|
+
the fallback strictly tighter than the CJS resolution it backs up (#4719's
|
|
585
|
+
declaration gate, from the fallback side). What moved is the EXPECTATION, still
|
|
586
|
+
authored by the host and still read out of the host's own `package.json`: an
|
|
587
|
+
alias naming one package refuses a directory holding another, a non-aliased
|
|
588
|
+
declaration is unchanged, and a value that is not a bare package name — a
|
|
589
|
+
`workspace:` range, an alias carrying a subpath — yields no expectation to move
|
|
590
|
+
to, so the key stays and today's refusal is kept. `link:` and `file:` name a
|
|
591
|
+
LOCATION rather than a package, so no name is derivable from them at all; they
|
|
592
|
+
keep the key expectation, and with it the conservative direction the finder had
|
|
593
|
+
before.
|
|
594
|
+
- a81aa9d: fix(types): a DEMOTED `declaredCode` is withheld on a 5xx the producer did not declare (#12509, ADR-0112)
|
|
595
|
+
|
|
596
|
+
**Wire change, for undeclared server faults only.** When a 5xx has its prose
|
|
597
|
+
withheld, the producer's demoted `declaredCode` is now withheld with it —
|
|
598
|
+
but only when the fallback-to-500 picked that code up from a producer that
|
|
599
|
+
declared no HTTP answer. An author-declared code is untouched at every status.
|
|
600
|
+
|
|
601
|
+
FROM (`origin/main`, measured through the real routes):
|
|
602
|
+
|
|
603
|
+
```
|
|
604
|
+
POST /api/v1/packages/publish → 500 {"error":{"code":"INTERNAL_ERROR",
|
|
605
|
+
"message":"Internal server error","declaredCode":"SQLITE_ERROR"}}
|
|
606
|
+
POST /api/v1/analytics/query → 500 {"error":{"code":"INTERNAL_ERROR",
|
|
607
|
+
"message":"Internal server error","httpStatus":500,"declaredCode":"42P01"}}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
TO:
|
|
611
|
+
|
|
612
|
+
```
|
|
613
|
+
POST /api/v1/packages/publish → 500 {"error":{"code":"INTERNAL_ERROR",
|
|
614
|
+
"message":"Internal server error"}}
|
|
615
|
+
POST /api/v1/analytics/query → 500 {"error":{"code":"INTERNAL_ERROR",
|
|
616
|
+
"message":"Internal server error","httpStatus":500}}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
UNCHANGED — the author-authored channel the ADR-0112 amendment wrote
|
|
620
|
+
`declaredCode` for:
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
{ status: 503, code: 'ACME_LEDGER_OFFLINE' }
|
|
624
|
+
→ 503 {"error":{"code":"SERVICE_UNAVAILABLE",…,"declaredCode":"ACME_LEDGER_OFFLINE"}}
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
`SQLITE_ERROR` vs `42P01` names the backend, which is one of the two
|
|
628
|
+
disclosures the 5xx message withhold exists to prevent (the other,
|
|
629
|
+
identifiers, was already covered). Maintainer ruling 2026-08-27, option D.
|
|
630
|
+
|
|
631
|
+
**What a consumer must know.** A `declaredCode` on a 5xx now means the
|
|
632
|
+
producer declared that fault itself, which is a stronger guarantee than the
|
|
633
|
+
field carried before; nothing that was a *registered* code moves, and no 4xx
|
|
634
|
+
moves. A producer that spells a code but declares no status loses that code
|
|
635
|
+
on a 5xx — declare the status the refusal means and the spelling is kept.
|
|
636
|
+
|
|
637
|
+
The distinction lives in ONE place, `serverFaultProvenance`
|
|
638
|
+
(`packages/types/src/thrown-http-error.ts`), read by `demotedDeclaredCode` —
|
|
639
|
+
the read every door already makes — so all five emitting exits inherit it and
|
|
640
|
+
no registrar carries a variant. The prose axis of the same ruling (the
|
|
641
|
+
dispatcher door adopting the structural withhold for every declared 5xx
|
|
642
|
+
message) is #12281 and is deliberately not applied here.
|
|
643
|
+
- 56c093c: fix(driver-memory): enforce field-level `unique`, so a colliding write is refused instead of landing silently (#13197)
|
|
644
|
+
|
|
645
|
+
`InMemoryDriver` enforced **no uniqueness at all**. `create` was a
|
|
646
|
+
`table.push()` and `syncSchema` allocated an array, so a `unique: true` field
|
|
647
|
+
was declared-and-not-enforced — the ADR-0078 / Prime-Directive-#10 shape the
|
|
648
|
+
platform refuses everywhere else. A colliding write did not fail; it landed, and
|
|
649
|
+
a read returned both rows.
|
|
650
|
+
|
|
651
|
+
The motivating instance is the worst-shaped one. The engine's
|
|
652
|
+
`createWithAutonumberResync` re-seeds the counter and re-issues a record number
|
|
653
|
+
when the STORE rejects it as a duplicate, so on a store that rejected nothing
|
|
654
|
+
the whole branch was unreachable: an autonumber allocated out of process
|
|
655
|
+
duplicated an existing business identifier with **no error anywhere**. The
|
|
656
|
+
remedy's location was already ruled in-tree at that method — «uniqueness
|
|
657
|
+
enforcement in the driver, NOT a pre-issue existence probe here» — and this is
|
|
658
|
+
that remedy. Nothing in the new code knows what an autonumber is; the defect was
|
|
659
|
+
that the driver constrained nothing.
|
|
660
|
+
|
|
661
|
+
**The refusal** carries the ADR-0112 envelope the SQL family answers a conflict
|
|
662
|
+
with: `code: 'UNIQUE_VIOLATION'`, `status: 409`, no `[driver-memory]` prefix. So
|
|
663
|
+
a suite that swaps this driver for SQLite sees one envelope — the parity
|
|
664
|
+
`memory-filter-refusal-envelope.test.ts` already states for the filter family,
|
|
665
|
+
now held for the constraint family. It is checked before the row is written, so
|
|
666
|
+
a refused write leaves the table exactly as it found it, and `updateMany`
|
|
667
|
+
prepares and checks the whole batch before mutating any of it.
|
|
668
|
+
|
|
669
|
+
**The scoping is `driver-sql`'s, measured — not a simpler invention.** Read off
|
|
670
|
+
`uniqueIndexesFromFields` (ADR-0120 D1/D3) and reproduced arm for arm:
|
|
671
|
+
`unique: 'global'` is platform-wide; bare `true` and `'organization'` are
|
|
672
|
+
per-organization (bare `true` is the POSITIONAL spelling of `'organization'` at
|
|
673
|
+
FIELD level — reading it as `'global'` is the #4986 trap and would make two
|
|
674
|
+
organizations' identical values collide on a constraint neither can see); both
|
|
675
|
+
degrade to a single column when the object has no tenant column, and a `unique`
|
|
676
|
+
declaration on the tenant column itself stays single-column. NULL values stay
|
|
677
|
+
NULL-DISTINCT, exactly as under SQL `UNIQUE`. The D3 NULL-organization fold
|
|
678
|
+
needs no `'__global__'` token here — that sentinel is a SQL-expression artefact,
|
|
679
|
+
and a JavaScript key holds `null` directly.
|
|
680
|
+
|
|
681
|
+
**Not** widened into: object-level declared `indexes[]` (composite uniques),
|
|
682
|
+
primary keys, or row-level tenant isolation. This driver still refuses to boot
|
|
683
|
+
multi-tenant (#6915) and that guard is untouched.
|
|
684
|
+
|
|
685
|
+
`@objectstack/types` (`patch`): `isUniqueViolationError` now reads the
|
|
686
|
+
platform's own registered `UNIQUE_VIOLATION` code on the `code` channel. Not
|
|
687
|
+
cosmetic — a conflict that predicate does not recognise leaves the autonumber
|
|
688
|
+
resync unable to re-seed, so the counter stays warm and every following insert
|
|
689
|
+
collides too (#5495's PROBE3 storm), i.e. a silent duplicate traded for a
|
|
690
|
+
non-converging insert loop. It is a tautology rather than a widened heuristic
|
|
691
|
+
(the code already MEANS this condition), and no existing in-repo producer's
|
|
692
|
+
classification changes: `@objectstack/rest`'s own response body is the only
|
|
693
|
+
other site carrying that string, and it is downstream of the predicate.
|
|
694
|
+
|
|
695
|
+
**Grade.** `minor` for the driver, not `patch`: a write that previously
|
|
696
|
+
succeeded is now refused (`409`), which is an accept-set narrowing under the
|
|
697
|
+
repo's launch-window convention for breaking changes, and the package also gains
|
|
698
|
+
public exports (`UNIQUE_VIOLATION_CODE`, `uniqueConstraintsFromFields`,
|
|
699
|
+
`tenantFieldOf`, `uniqueKeyOf`, `assertNoUniqueViolation`,
|
|
700
|
+
`uniqueViolationError`). `patch` for `@objectstack/types`: no API added or
|
|
701
|
+
removed and no in-repo verdict changes — the limb exists to serve the new
|
|
702
|
+
producer. Fixtures that relied on duplicates landing on a declared-unique field
|
|
703
|
+
must stop declaring `unique`, or stop writing the duplicate; the repo's own
|
|
704
|
+
suites were measured and none did.
|
|
705
|
+
- bfe13c8: fix(types,cli): resolve host-declared packages through the `import` condition, and read the cluster registry instead of assuming it (#13330)
|
|
706
|
+
|
|
707
|
+
`createHostImporter`'s declared leg resolved with `hostRequire.resolve(pkg)` — a
|
|
708
|
+
**CommonJS** resolution, which answers the `require` condition. Every `tsup`
|
|
709
|
+
dual build publishes `{ "import": "./dist/index.js", "require": "./dist/index.cjs" }`,
|
|
710
|
+
so a package loaded through that leg evaluated as its **CommonJS** build while
|
|
711
|
+
the callers (`packages/cli` is `"type": "module"`) held the **ESM** build of the
|
|
712
|
+
same package. The process ended up with two instances of everything the loaded
|
|
713
|
+
package shares with its caller, each with its own module-scope state.
|
|
714
|
+
|
|
715
|
+
Measured consequence, on the shipped EE multi-node path (ADR-0018): `os serve`
|
|
716
|
+
loaded `@objectstack/service-cluster-redis` through this leg, the driver's
|
|
717
|
+
load-time `registerClusterDriver('redis', …)` ran against the CommonJS copy of
|
|
718
|
+
`@objectstack/service-cluster`, and the ESM `Runtime` read the ESM copy and
|
|
719
|
+
found nothing — `OS_CLUSTER_DRIVER=redis` died at `defineCluster()` with
|
|
720
|
+
`Cluster driver "redis" is not registered`, about a package that was installed,
|
|
721
|
+
declared and resolvable. Any module-scope registry crossing this seam had the
|
|
722
|
+
same defect; the cluster driver is the instance that shipped.
|
|
723
|
+
|
|
724
|
+
**The seam.** The declared leg now imports the entry the `import` condition
|
|
725
|
+
names. The host anchor is untouched — the CJS resolver still answers *where*
|
|
726
|
+
the package is, because no flagless Node API resolves a bare specifier against
|
|
727
|
+
an arbitrary parent; only the *condition* is re-decided, by reading that
|
|
728
|
+
package's own `exports` map. Deliberately narrow at the **resolution** level —
|
|
729
|
+
no load that works today resolves differently unless the package itself
|
|
730
|
+
publishes a valid, existing import-condition target: a package with no
|
|
731
|
+
`exports` map is untouched (CJS resolution already returned `main`), a package
|
|
732
|
+
publishing no import-condition target is untouched, and anything unreadable or
|
|
733
|
+
absent on disk falls back to the CJS-resolved path. That narrowness does not
|
|
734
|
+
extend to **evaluation**: a dual-published package whose `import` build exists
|
|
735
|
+
but throws while its `require` build works used to mask that break by silently
|
|
736
|
+
loading the CJS build, and now surfaces it — arguably the correct reading of a
|
|
737
|
+
broken published build, but a behaviour change, not a no-op.
|
|
738
|
+
|
|
739
|
+
**The reading.** A residual split is still possible above the seam — two
|
|
740
|
+
*physical* copies of one package are two instances in any module system, and no
|
|
741
|
+
resolver condition merges them — so `os serve` no longer assumes the driver
|
|
742
|
+
registered. `@objectstack/service-cluster` exports `listClusterDrivers()`, the
|
|
743
|
+
registry `defineCluster()` itself consults, and `serve` queries it after the
|
|
744
|
+
load. The silent `catch` is gone: a driver that loaded but stayed invisible, one
|
|
745
|
+
that could not be resolved, and one that resolved and then crashed now read as
|
|
746
|
+
three different diagnoses instead of arriving as `not registered` one line
|
|
747
|
+
later. An app on an older `@objectstack/service-cluster` has no accessor to
|
|
748
|
+
call; that case is silent — `serve` declines to claim either answer rather
|
|
749
|
+
than printing one.
|
|
750
|
+
|
|
751
|
+
No behaviour downstream of the diagnosis changed: an absent driver still reaches
|
|
752
|
+
`defineCluster()`'s documented error (`cluster.mdx` §8.1) rather than silently
|
|
753
|
+
downgrading to the in-memory cluster, and the only documented downgrade here —
|
|
754
|
+
a multi-node gate denial — is untouched.
|
|
755
|
+
- 22e5236: fix(types,runtime): log every 5xx at `error` level instead of answering it silently (#14310)
|
|
756
|
+
|
|
757
|
+
A 500 that leaves no server-side line is diagnosed from the browser or not at
|
|
758
|
+
all. Measured on `main`, through the real plugin and the real route handlers: a
|
|
759
|
+
plain `Error` thrown out of a dispatcher route answered `500 INTERNAL_ERROR`
|
|
760
|
+
with **zero** log records at any level — the only evidence was the client's
|
|
761
|
+
console and the response body. That is AGENTS.md "Route & surface ownership §3
|
|
762
|
+
— absence must be loud" inverted, and it is why a `/api/v1/packages` regression
|
|
763
|
+
stayed invisible for a week.
|
|
764
|
+
|
|
765
|
+
The reporting that already existed was not a substitute, for two independent
|
|
766
|
+
reasons:
|
|
767
|
+
|
|
768
|
+
- `ErrorReporter.captureException` defaults to `NoopErrorReporter`. A dev
|
|
769
|
+
server — the surface an operator actually watches — wires no APM, so the
|
|
770
|
+
capture was a no-op every time. A log line is the operator's floor; APM is
|
|
771
|
+
opt-in telemetry on top of it.
|
|
772
|
+
- It is fed by `res.__obsRecordedError`, which only the THROWN exit sets. A
|
|
773
|
+
route that catches its own fault and RETURNS a 5xx envelope — how every
|
|
774
|
+
`/packages` handler answers, via `deps.errorFromThrown` — recorded nothing,
|
|
775
|
+
so even a wired reporter never saw those.
|
|
776
|
+
|
|
777
|
+
**The rule now has one definition.** `logServerFault` (new, in
|
|
778
|
+
`@objectstack/types`) emits exactly one `error`-level record carrying method,
|
|
779
|
+
path, request id, the message and — where the door still holds the throw — the
|
|
780
|
+
stack. It shares a home with `resolveThrownHttpError` for the same reason that
|
|
781
|
+
rule was moved there in #8016: a rule two doors must agree on cannot live
|
|
782
|
+
inside one of them, because `@objectstack/runtime` depends on
|
|
783
|
+
`@objectstack/rest` and an import could only ever point one way.
|
|
784
|
+
|
|
785
|
+
Wired at each transport's single exit, so a fault costs one line and never two:
|
|
786
|
+
|
|
787
|
+
- `sendError` — the one writer for every nested-envelope error in the repo. The
|
|
788
|
+
REST direct-mount registrars (the `/api/v1/packages` door that mounts first
|
|
789
|
+
in production) become loud through it with no per-door call, so a door added
|
|
790
|
+
later cannot forget one.
|
|
791
|
+
- The dispatcher's thrown exit (`errorResponseBase`), its returned exit
|
|
792
|
+
(`sendResultBase`) and the AI-route mount that writes its own result.
|
|
793
|
+
|
|
794
|
+
`packages/rest`'s `/data` doors were already loud via `logUnexpectedRouteError`
|
|
795
|
+
and are untouched.
|
|
796
|
+
|
|
797
|
+
`error` level is load-bearing: the CLI's default is `warn` and `error` (40)
|
|
798
|
+
outranks `warn` (30), so the record clears `--log-level`'s default without
|
|
799
|
+
bypassing the level system. `--log-level silent` still silences it, which is a
|
|
800
|
+
deliberate instruction rather than the default this fixes.
|
|
801
|
+
|
|
802
|
+
**4xx stays quiet**, decided once inside the helper rather than at each call
|
|
803
|
+
site — client mistakes are already explained by the response, and logging them
|
|
804
|
+
is how a `?state=draft` probe once printed 45 stack traces in one browsing
|
|
805
|
+
session. The wire body is byte-identical at every door: this adds a side
|
|
806
|
+
effect, never a field.
|
|
807
|
+
|
|
808
|
+
⚠️ Behaviour change worth knowing before upgrading: a deployment that answers
|
|
809
|
+
a *declared* 5xx on a polled route — `501 NOT_IMPLEMENTED` from an uninstalled
|
|
810
|
+
optional service, say — now prints one `error` line per request where it
|
|
811
|
+
previously printed none. The band is the one the issue specifies ("4xx may stay
|
|
812
|
+
quiet; 5xx never"); narrowing it for declared capability-absence would be a
|
|
813
|
+
separate contract decision.
|
|
814
|
+
- 2cf5a96: An organization no longer stops accepting members at 100 — membership is not a
|
|
815
|
+
limited axis, and the ceiling nobody chose is now stated explicitly
|
|
816
|
+
|
|
817
|
+
A customer adding users was refused with `Organization membership limit
|
|
818
|
+
reached`. Nothing in this codebase set that ceiling: better-auth's organization
|
|
819
|
+
plugin substitutes a vendor default of **100** for an absent `membershipLimit`
|
|
820
|
+
(`count >= (membershipLimit || 100)` in `routes/crud-members`), and
|
|
821
|
+
`auth-manager` passed `organizationLimit` — how many organizations one user may
|
|
822
|
+
CREATE — while never passing `membershipLimit`, which is a different question.
|
|
823
|
+
|
|
824
|
+
The two read almost identically in a config block and mean nothing alike, which
|
|
825
|
+
is why the gap survived: the option that WAS set looked like the option that
|
|
826
|
+
was not. In the field the refusal is worse than merely wrong — it arrives while
|
|
827
|
+
an operator is looking at licences and seat counts, and reads as an entitlement
|
|
828
|
+
problem on an axis that carries no entitlement at all. Seats are metered on AI
|
|
829
|
+
usage; plain membership has never been billed.
|
|
830
|
+
|
|
831
|
+
- `membershipLimit` is now passed explicitly, defaulting to unbounded.
|
|
832
|
+
- `OS_ORG_MEMBERSHIP_LIMIT` is the opt-in for a deployment that DOES want a
|
|
833
|
+
ceiling (a pilot, a trial tenant). Unusable values (empty, non-numeric,
|
|
834
|
+
zero, negative) read as unset rather than as a cap — a typo must not be the
|
|
835
|
+
thing that locks an organization, which is exactly the failure mode being
|
|
836
|
+
fixed.
|
|
837
|
+
- The decision lives in `resolveMembershipLimitOption()` rather than inside the
|
|
838
|
+
plugin-construction expression, so it is testable: the unset case, the
|
|
839
|
+
explicit ceiling, the unusable-value direction, and — deliberately — that the
|
|
840
|
+
chosen value clears the vendor's 100 by a wide margin. If a future
|
|
841
|
+
better-auth changes that default, the test says so instead of leaving an
|
|
842
|
+
unexplained constant behind.
|
|
843
|
+
|
|
844
|
+
The unbounded value is `Number.MAX_SAFE_INTEGER`, not `Infinity`: the option is
|
|
845
|
+
compared numerically but also travels through option plumbing that may assume a
|
|
846
|
+
finite value, and nine quadrillion members is unlimited by any measure that
|
|
847
|
+
reaches a real deployment.
|
|
848
|
+
- Updated dependencies [809d417]
|
|
849
|
+
- Updated dependencies [387e231]
|
|
850
|
+
- Updated dependencies [f794e4e]
|
|
851
|
+
- Updated dependencies [cae2169]
|
|
852
|
+
- Updated dependencies [b812a54]
|
|
853
|
+
- Updated dependencies [2d4fa75]
|
|
854
|
+
- Updated dependencies [0e4e51b]
|
|
855
|
+
- Updated dependencies [e84bbf6]
|
|
856
|
+
- Updated dependencies [effae80]
|
|
857
|
+
- Updated dependencies [d62f990]
|
|
858
|
+
- Updated dependencies [c45d8e6]
|
|
859
|
+
- Updated dependencies [2e3e8c7]
|
|
860
|
+
- Updated dependencies [e621291]
|
|
861
|
+
- Updated dependencies [40a93b5]
|
|
862
|
+
- Updated dependencies [d5b330d]
|
|
863
|
+
- Updated dependencies [dda969c]
|
|
864
|
+
- Updated dependencies [1f45690]
|
|
865
|
+
- Updated dependencies [277948f]
|
|
866
|
+
- Updated dependencies [8bdd955]
|
|
867
|
+
- Updated dependencies [f3bbbef]
|
|
868
|
+
- Updated dependencies [4f24e9d]
|
|
869
|
+
- Updated dependencies [474242f]
|
|
870
|
+
- Updated dependencies [63cd487]
|
|
871
|
+
- Updated dependencies [bd4aa4e]
|
|
872
|
+
- Updated dependencies [803eaab]
|
|
873
|
+
- Updated dependencies [f8e8f03]
|
|
874
|
+
- Updated dependencies [eae824e]
|
|
875
|
+
- Updated dependencies [f6fa22c]
|
|
876
|
+
- Updated dependencies [8a483b3]
|
|
877
|
+
- Updated dependencies [97bcd99]
|
|
878
|
+
- Updated dependencies [df59de0]
|
|
879
|
+
- Updated dependencies [96e25a8]
|
|
880
|
+
- Updated dependencies [f75a38a]
|
|
881
|
+
- Updated dependencies [7a25e7d]
|
|
882
|
+
- Updated dependencies [1fa05a6]
|
|
883
|
+
- Updated dependencies [c85a265]
|
|
884
|
+
- Updated dependencies [dcb10a5]
|
|
885
|
+
- Updated dependencies [773a999]
|
|
886
|
+
- Updated dependencies [35dffea]
|
|
887
|
+
- Updated dependencies [776a098]
|
|
888
|
+
- Updated dependencies [5060877]
|
|
889
|
+
- Updated dependencies [4f6325d]
|
|
890
|
+
- Updated dependencies [52954c0]
|
|
891
|
+
- Updated dependencies [2aa8456]
|
|
892
|
+
- Updated dependencies [93809a3]
|
|
893
|
+
- Updated dependencies [7c0d0c3]
|
|
894
|
+
- Updated dependencies [daae7aa]
|
|
895
|
+
- Updated dependencies [8dc22d6]
|
|
896
|
+
- Updated dependencies [279431e]
|
|
897
|
+
- Updated dependencies [948dd6b]
|
|
898
|
+
- Updated dependencies [3b4c56c]
|
|
899
|
+
- Updated dependencies [ae8edd2]
|
|
900
|
+
- Updated dependencies [e25403c]
|
|
901
|
+
- Updated dependencies [64baa68]
|
|
902
|
+
- Updated dependencies [9fa70d7]
|
|
903
|
+
- Updated dependencies [09db64a]
|
|
904
|
+
- Updated dependencies [92916e7]
|
|
905
|
+
- Updated dependencies [a84f3ea]
|
|
906
|
+
- Updated dependencies [f2eaae8]
|
|
907
|
+
- Updated dependencies [c09451b]
|
|
908
|
+
- Updated dependencies [ba64877]
|
|
909
|
+
- Updated dependencies [7345308]
|
|
910
|
+
- Updated dependencies [79b6a22]
|
|
911
|
+
- Updated dependencies [30d96ab]
|
|
912
|
+
- Updated dependencies [f658793]
|
|
913
|
+
- Updated dependencies [c95ad19]
|
|
914
|
+
- Updated dependencies [e58ea8b]
|
|
915
|
+
- Updated dependencies [4a17645]
|
|
916
|
+
- Updated dependencies [3795c5f]
|
|
917
|
+
- Updated dependencies [8ab926b]
|
|
918
|
+
- Updated dependencies [7317cf2]
|
|
919
|
+
- Updated dependencies [e25e839]
|
|
920
|
+
- Updated dependencies [5997207]
|
|
921
|
+
- Updated dependencies [8b13cc8]
|
|
922
|
+
- Updated dependencies [4a4a35d]
|
|
923
|
+
- Updated dependencies [86e765a]
|
|
924
|
+
- Updated dependencies [1d7e76a]
|
|
925
|
+
- Updated dependencies [53dc739]
|
|
926
|
+
- Updated dependencies [fd289be]
|
|
927
|
+
- Updated dependencies [03bf7b1]
|
|
928
|
+
- Updated dependencies [f90e820]
|
|
929
|
+
- Updated dependencies [18d816a]
|
|
930
|
+
- Updated dependencies [e8bd715]
|
|
931
|
+
- Updated dependencies [b91c351]
|
|
932
|
+
- Updated dependencies [a28a3c0]
|
|
933
|
+
- Updated dependencies [daeaaf9]
|
|
934
|
+
- Updated dependencies [c459da6]
|
|
935
|
+
- Updated dependencies [e914733]
|
|
936
|
+
- Updated dependencies [f887e52]
|
|
937
|
+
- Updated dependencies [881f8d8]
|
|
938
|
+
- Updated dependencies [3bfa1e6]
|
|
939
|
+
- Updated dependencies [901355c]
|
|
940
|
+
- Updated dependencies [34ce8e7]
|
|
941
|
+
- Updated dependencies [33681ea]
|
|
942
|
+
- Updated dependencies [4635f3e]
|
|
943
|
+
- Updated dependencies [ee3595c]
|
|
944
|
+
- Updated dependencies [b2eab95]
|
|
945
|
+
- Updated dependencies [93940d4]
|
|
946
|
+
- Updated dependencies [3a04b01]
|
|
947
|
+
- Updated dependencies [45b9051]
|
|
948
|
+
- Updated dependencies [b9e9227]
|
|
949
|
+
- Updated dependencies [d395692]
|
|
950
|
+
- Updated dependencies [5894d30]
|
|
951
|
+
- Updated dependencies [a3765f6]
|
|
952
|
+
- Updated dependencies [e22158f]
|
|
953
|
+
- Updated dependencies [7404925]
|
|
954
|
+
- Updated dependencies [0c2334f]
|
|
955
|
+
- Updated dependencies [778c59f]
|
|
956
|
+
- Updated dependencies [d2619fd]
|
|
957
|
+
- Updated dependencies [6acb11a]
|
|
958
|
+
- Updated dependencies [33c5fd3]
|
|
959
|
+
- Updated dependencies [20b0fdb]
|
|
960
|
+
- Updated dependencies [905019b]
|
|
961
|
+
- Updated dependencies [a286411]
|
|
962
|
+
- Updated dependencies [98c0d33]
|
|
963
|
+
- Updated dependencies [368a82e]
|
|
964
|
+
- Updated dependencies [a3d5724]
|
|
965
|
+
- Updated dependencies [93ea19b]
|
|
966
|
+
- Updated dependencies [9ee2dcf]
|
|
967
|
+
- Updated dependencies [8cb96ec]
|
|
968
|
+
- Updated dependencies [8f10a79]
|
|
969
|
+
- Updated dependencies [6269a55]
|
|
970
|
+
- Updated dependencies [0fb8760]
|
|
971
|
+
- Updated dependencies [e5ce2ed]
|
|
972
|
+
- Updated dependencies [be21955]
|
|
973
|
+
- Updated dependencies [bc56e18]
|
|
974
|
+
- Updated dependencies [be21955]
|
|
975
|
+
- Updated dependencies [a9ee989]
|
|
976
|
+
- Updated dependencies [4d0d944]
|
|
977
|
+
- Updated dependencies [15d58db]
|
|
978
|
+
- Updated dependencies [d63b014]
|
|
979
|
+
- Updated dependencies [9abe4e4]
|
|
980
|
+
- Updated dependencies [2cc7122]
|
|
981
|
+
- Updated dependencies [50d6c92]
|
|
982
|
+
- Updated dependencies [9e0ba21]
|
|
983
|
+
- Updated dependencies [311433f]
|
|
984
|
+
- Updated dependencies [3e5ad08]
|
|
985
|
+
- Updated dependencies [9abe4e4]
|
|
986
|
+
- Updated dependencies [b7131f3]
|
|
987
|
+
- Updated dependencies [e5812fa]
|
|
988
|
+
- Updated dependencies [7085f90]
|
|
989
|
+
- Updated dependencies [dee4dd4]
|
|
990
|
+
- Updated dependencies [ce7e497]
|
|
991
|
+
- Updated dependencies [51ecb2f]
|
|
992
|
+
- Updated dependencies [9086761]
|
|
993
|
+
- Updated dependencies [42a117b]
|
|
994
|
+
- Updated dependencies [1401ae7]
|
|
995
|
+
- Updated dependencies [4297fe7]
|
|
996
|
+
- Updated dependencies [e398863]
|
|
997
|
+
- Updated dependencies [d16df74]
|
|
998
|
+
- Updated dependencies [f11fc61]
|
|
999
|
+
- Updated dependencies [e808890]
|
|
1000
|
+
- Updated dependencies [8f79379]
|
|
1001
|
+
- Updated dependencies [e6ca40e]
|
|
1002
|
+
- Updated dependencies [0c77ea4]
|
|
1003
|
+
- Updated dependencies [52954c0]
|
|
1004
|
+
- Updated dependencies [89eb997]
|
|
1005
|
+
- Updated dependencies [aa5994e]
|
|
1006
|
+
- Updated dependencies [be93457]
|
|
1007
|
+
- Updated dependencies [a65db76]
|
|
1008
|
+
- Updated dependencies [15eb2c9]
|
|
1009
|
+
- Updated dependencies [5691b07]
|
|
1010
|
+
- Updated dependencies [2a6122b]
|
|
1011
|
+
- Updated dependencies [225e769]
|
|
1012
|
+
- Updated dependencies [8af88dd]
|
|
1013
|
+
- Updated dependencies [fb5fbb8]
|
|
1014
|
+
- Updated dependencies [d7b3963]
|
|
1015
|
+
- Updated dependencies [b72db01]
|
|
1016
|
+
- Updated dependencies [dce5cd4]
|
|
1017
|
+
- Updated dependencies [177ebdc]
|
|
1018
|
+
- Updated dependencies [8d237b4]
|
|
1019
|
+
- Updated dependencies [2d2e6f0]
|
|
1020
|
+
- Updated dependencies [2d8dd8d]
|
|
1021
|
+
- Updated dependencies [22d573e]
|
|
1022
|
+
- Updated dependencies [b5a2398]
|
|
1023
|
+
- Updated dependencies [348860c]
|
|
1024
|
+
- Updated dependencies [5383fa6]
|
|
1025
|
+
- Updated dependencies [5b3ff63]
|
|
1026
|
+
- Updated dependencies [1a6a19c]
|
|
1027
|
+
- Updated dependencies [527e050]
|
|
1028
|
+
- Updated dependencies [dd33bf9]
|
|
1029
|
+
- Updated dependencies [4cb2a90]
|
|
1030
|
+
- Updated dependencies [74a7804]
|
|
1031
|
+
- Updated dependencies [53d3689]
|
|
1032
|
+
- Updated dependencies [b3a63d3]
|
|
1033
|
+
- Updated dependencies [033a34c]
|
|
1034
|
+
- Updated dependencies [4d25d22]
|
|
1035
|
+
- Updated dependencies [1ffee51]
|
|
1036
|
+
- Updated dependencies [5ae4303]
|
|
1037
|
+
- Updated dependencies [ece4dad]
|
|
1038
|
+
- Updated dependencies [e9b377e]
|
|
1039
|
+
- Updated dependencies [146f448]
|
|
1040
|
+
- Updated dependencies [735f5c7]
|
|
1041
|
+
- Updated dependencies [a7e18de]
|
|
1042
|
+
- Updated dependencies [366f895]
|
|
1043
|
+
- Updated dependencies [dc75ba8]
|
|
1044
|
+
- Updated dependencies [cce0aa9]
|
|
1045
|
+
- Updated dependencies [e764507]
|
|
1046
|
+
- Updated dependencies [cff17af]
|
|
1047
|
+
- Updated dependencies [39404f3]
|
|
1048
|
+
- Updated dependencies [ca1965f]
|
|
1049
|
+
- Updated dependencies [8619f95]
|
|
1050
|
+
- Updated dependencies [b706af9]
|
|
1051
|
+
- Updated dependencies [fc9ba76]
|
|
1052
|
+
- Updated dependencies [0f94cc7]
|
|
1053
|
+
- Updated dependencies [a11c1a5]
|
|
1054
|
+
- Updated dependencies [71f9cd1]
|
|
1055
|
+
- Updated dependencies [ee17d86]
|
|
1056
|
+
- Updated dependencies [cdbd920]
|
|
1057
|
+
- Updated dependencies [18c432e]
|
|
1058
|
+
- Updated dependencies [3c418c4]
|
|
1059
|
+
- Updated dependencies [fa8715a]
|
|
1060
|
+
- Updated dependencies [a933ed7]
|
|
1061
|
+
- Updated dependencies [b3ca463]
|
|
1062
|
+
- Updated dependencies [a933ed7]
|
|
1063
|
+
- Updated dependencies [0d4a6a8]
|
|
1064
|
+
- Updated dependencies [518d5e5]
|
|
1065
|
+
- Updated dependencies [6643ba1]
|
|
1066
|
+
- Updated dependencies [eeba2ef]
|
|
1067
|
+
- Updated dependencies [ec4c4d2]
|
|
1068
|
+
- Updated dependencies [424f73c]
|
|
1069
|
+
- Updated dependencies [cccbe51]
|
|
1070
|
+
- Updated dependencies [a8d6b1d]
|
|
1071
|
+
- Updated dependencies [e4a7695]
|
|
1072
|
+
- Updated dependencies [87075b1]
|
|
1073
|
+
- Updated dependencies [fc58a99]
|
|
1074
|
+
- Updated dependencies [14cfc00]
|
|
1075
|
+
- Updated dependencies [1c6f7b4]
|
|
1076
|
+
- Updated dependencies [e854a53]
|
|
1077
|
+
- Updated dependencies [dfebfc8]
|
|
1078
|
+
- Updated dependencies [d028b37]
|
|
1079
|
+
- Updated dependencies [122ef38]
|
|
1080
|
+
- Updated dependencies [4a37870]
|
|
1081
|
+
- Updated dependencies [428f9b2]
|
|
1082
|
+
- Updated dependencies [aa7ff56]
|
|
1083
|
+
- Updated dependencies [c41b42e]
|
|
1084
|
+
- Updated dependencies [c4db311]
|
|
1085
|
+
- Updated dependencies [750fff5]
|
|
1086
|
+
- Updated dependencies [c19035e]
|
|
1087
|
+
- Updated dependencies [ececf7a]
|
|
1088
|
+
- Updated dependencies [d173125]
|
|
1089
|
+
- Updated dependencies [8eeca27]
|
|
1090
|
+
- Updated dependencies [8425c17]
|
|
1091
|
+
- Updated dependencies [a5ef1d8]
|
|
1092
|
+
- Updated dependencies [772d5de]
|
|
1093
|
+
- Updated dependencies [ce80ec2]
|
|
1094
|
+
- Updated dependencies [b372318]
|
|
1095
|
+
- Updated dependencies [97a2263]
|
|
1096
|
+
- Updated dependencies [29d0676]
|
|
1097
|
+
- Updated dependencies [0169d49]
|
|
1098
|
+
- Updated dependencies [6bd3231]
|
|
1099
|
+
- Updated dependencies [d2b5ba8]
|
|
1100
|
+
- Updated dependencies [b799ac5]
|
|
1101
|
+
- Updated dependencies [8f74307]
|
|
1102
|
+
- Updated dependencies [d23dc08]
|
|
1103
|
+
- Updated dependencies [644ad50]
|
|
1104
|
+
- Updated dependencies [0da7cd2]
|
|
1105
|
+
- Updated dependencies [28a5c3e]
|
|
1106
|
+
- Updated dependencies [4bc18e5]
|
|
1107
|
+
- @objectstack/spec@17.3.0
|
|
1108
|
+
|
|
3
1109
|
## 17.2.0
|
|
4
1110
|
|
|
5
1111
|
### Minor Changes
|