@objectstack/metadata 17.3.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 +532 -0
- package/README.md +6 -4
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +299 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +227 -60
- package/dist/index.d.ts +227 -60
- package/dist/index.js +294 -35
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.cjs +104 -22
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +40 -1
- package/dist/migrations/index.d.ts +40 -1
- package/dist/migrations/index.js +107 -22
- package/dist/migrations/index.js.map +1 -1
- package/dist/node.cjs +299 -36
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +24 -1
- package/dist/node.d.ts +24 -1
- package/dist/node.js +294 -35
- package/dist/node.js.map +1 -1
- package/dist/view-container.cjs +37 -0
- package/dist/view-container.cjs.map +1 -0
- package/dist/view-container.d.cts +76 -0
- package/dist/view-container.d.ts +76 -0
- package/dist/view-container.js +12 -0
- package/dist/view-container.js.map +1 -0
- package/package.json +18 -8
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,7 @@ import { Logger, Plugin, PluginContext } from '@objectstack/core';
|
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { MetadataRepository } from '@objectstack/metadata-core';
|
|
11
11
|
export { HistoryOptions, MetaRef, MetadataEvent, MetadataItem, MetadataItemHeader, MetadataRepository, SysMetadataHistoryObject, SysMetadataObject, WatchFilter } from '@objectstack/metadata-core';
|
|
12
|
+
export { deriveViewContainerObject } from './view-container.cjs';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Metadata Serializer Interface
|
|
@@ -868,6 +869,30 @@ declare class MetadataManager implements IMetadataService {
|
|
|
868
869
|
exists(type: string, name: string): Promise<boolean>;
|
|
869
870
|
/**
|
|
870
871
|
* List all names of metadata items of a given type
|
|
872
|
+
*
|
|
873
|
+
* ## [#14423] One loader's fault does not take the whole enumeration down
|
|
874
|
+
*
|
|
875
|
+
* This loop used to be bare — `const result = await loader.list(type)` with
|
|
876
|
+
* no `try`, while the two sibling plural reads (`list()` via
|
|
877
|
+
* {@link admitLoaderItems}, and {@link loadMany}) have carried a per-loader
|
|
878
|
+
* `catch` since #5108. That asymmetry is the defect, independent of any one
|
|
879
|
+
* caller: the SAME storage outage was swallowed by one plural read and
|
|
880
|
+
* thrown out of the other, so which answer a caller got depended only on
|
|
881
|
+
* which method it happened to call. A caller reading both — the action
|
|
882
|
+
* governance audit is one — saw `loadMany` report a short-but-successful
|
|
883
|
+
* set and `listNames` throw, and had no way to tell that one fact was
|
|
884
|
+
* behind both.
|
|
885
|
+
*
|
|
886
|
+
* Same shape as `loadMany`'s, deliberately, down to the helpers: the outage
|
|
887
|
+
* is spoken once per loader through {@link reportLoaderReadFailure} and
|
|
888
|
+
* un-said through {@link reportLoaderReadRecovered}. ⛔ Not a third spelling
|
|
889
|
+
* for "a loader faulted" — a second vocabulary for one event is how the two
|
|
890
|
+
* reads drifted apart in the first place.
|
|
891
|
+
*
|
|
892
|
+
* The degradation is the same one `list()` documents and is graded the same
|
|
893
|
+
* way (AGENTS.md → "Degradation log levels"): the caller still gets an
|
|
894
|
+
* array, nothing 500s, and the set is quietly short — so it is reported at
|
|
895
|
+
* `error`, by the shared helper, rather than being re-graded here.
|
|
871
896
|
*/
|
|
872
897
|
listNames(type: string): Promise<string[]>;
|
|
873
898
|
/**
|
|
@@ -1181,6 +1206,72 @@ declare class MetadataManager implements IMetadataService {
|
|
|
1181
1206
|
* Aggregates results from all loaders.
|
|
1182
1207
|
*/
|
|
1183
1208
|
loadMany<T = any>(type: string, options?: MetadataLoadOptions): Promise<T[]>;
|
|
1209
|
+
/**
|
|
1210
|
+
* [#14423] {@link loadMany}, read under the identity the STORE holds each
|
|
1211
|
+
* item by — the keyed plural read, beside the unkeyed one.
|
|
1212
|
+
*
|
|
1213
|
+
* ## Why a second method and not a widened `loadMany`
|
|
1214
|
+
*
|
|
1215
|
+
* `loadMany` keys nothing: it returns bodies, and every consumer that needs
|
|
1216
|
+
* an identity reads `body.name` off them. #14205 already ruled what identity
|
|
1217
|
+
* IS — the key the store holds the item under (`register(type, name, data)`
|
|
1218
|
+
* takes it as the ARGUMENT, and a body is not required to name itself) — so
|
|
1219
|
+
* `body.name` is a guess that happens to be right for most items and drops
|
|
1220
|
+
* the rest ENTIRELY: an item whose body carries no `name` is served by
|
|
1221
|
+
* `load(type, name)` and is not nameable from `loadMany`'s answer at all.
|
|
1222
|
+
*
|
|
1223
|
+
* Widening `loadMany`'s return would fix that and break every consumer of a
|
|
1224
|
+
* published shape (the ones counted on this card all read `body.name` as the
|
|
1225
|
+
* identity). So this is additive: `loadMany`'s return shape is untouched,
|
|
1226
|
+
* and a caller that needs the key asks for the key.
|
|
1227
|
+
*
|
|
1228
|
+
* ## What it reads — the same population `loadMany` reads
|
|
1229
|
+
*
|
|
1230
|
+
* Loaders only, deliberately, so this is `loadMany` keyed and nothing more.
|
|
1231
|
+
* It is NOT `list()`/{@link listNames}, which also merge the in-memory
|
|
1232
|
+
* `register()` registry; a caller wanting that set has those. Reading the
|
|
1233
|
+
* loaders alone is also what makes this the enumerable twin of
|
|
1234
|
+
* {@link loadDiagnosed}, which walks the same loaders by name — that pairing
|
|
1235
|
+
* is the point on the audit side of #14423, where an enumeration and a
|
|
1236
|
+
* by-name read that disagree about a population make one subsystem accuse
|
|
1237
|
+
* another of a defect neither has.
|
|
1238
|
+
*
|
|
1239
|
+
* ## Delegate first, fall back second — and why that order is not a style
|
|
1240
|
+
*
|
|
1241
|
+
* Per loader: {@link MetadataLoader.loadManyKeyed} where the loader offers
|
|
1242
|
+
* one, else its `list()` + a per-name `load()`. Measured, on
|
|
1243
|
+
* `DatabaseLoader`: the keyed method shares `loadMany`'s single query
|
|
1244
|
+
* (`{find:1, findOne:0}` — zero extra cost), while enumerate-then-read-each
|
|
1245
|
+
* on that same loader is a real N+1 (`{find:1, findOne:5}` for five items).
|
|
1246
|
+
* The fallback exists for loaders that cannot produce keys at all
|
|
1247
|
+
* (`RemoteLoader`'s wire format carries bodies only), and it recovers the
|
|
1248
|
+
* nameless item the pre-#14205 `loadMany`-and-key-by-`body.name` fallback
|
|
1249
|
+
* drops — which is why it is `list()` + `load()` and not `loadMany()`.
|
|
1250
|
+
*
|
|
1251
|
+
* ## Failure posture
|
|
1252
|
+
*
|
|
1253
|
+
* Per-loader `try`/`catch`, the same seam and the same helpers as
|
|
1254
|
+
* {@link loadMany} and `list()` — one loader's outage does not take the
|
|
1255
|
+
* enumeration down, and it is reported once through
|
|
1256
|
+
* {@link reportLoaderReadFailure} rather than in a third vocabulary.
|
|
1257
|
+
* Earlier loaders win a key collision, mirroring `list()`.
|
|
1258
|
+
*/
|
|
1259
|
+
loadManyKeyed<T = any>(type: string, options?: MetadataLoadOptions): Promise<MetadataKeyedItem<T>[]>;
|
|
1260
|
+
/**
|
|
1261
|
+
* Merge ONE loader's answer for `type` into `items`, keyed by that loader's
|
|
1262
|
+
* own key for each item — {@link loadManyKeyed}'s per-loader body.
|
|
1263
|
+
*
|
|
1264
|
+
* Distinct from {@link admitLoaderItems} on exactly one axis, and that axis
|
|
1265
|
+
* is the whole of #14423: the fallback for a loader with no
|
|
1266
|
+
* `loadManyKeyed`. `admitLoaderItems` falls back to `loadMany` keyed by
|
|
1267
|
+
* `data.name` — the pre-#14205 behaviour, verbatim, which drops a nameless
|
|
1268
|
+
* body. Here the fallback is `list()` + a per-name `load()`, so a loader
|
|
1269
|
+
* that cannot enumerate keys and bodies together still answers with both.
|
|
1270
|
+
*
|
|
1271
|
+
* Read failures are NOT caught here — the caller owns that verdict, as in
|
|
1272
|
+
* {@link admitLoaderItems}.
|
|
1273
|
+
*/
|
|
1274
|
+
private admitKeyedLoaderItems;
|
|
1184
1275
|
/**
|
|
1185
1276
|
* Save metadata item to a loader
|
|
1186
1277
|
*/
|
|
@@ -1662,6 +1753,40 @@ declare class RemoteLoader implements MetadataLoader {
|
|
|
1662
1753
|
loadMany<T = any>(type: string, _options?: MetadataLoadOptions): Promise<T[]>;
|
|
1663
1754
|
exists(type: string, name: string): Promise<boolean>;
|
|
1664
1755
|
stat(type: string, name: string): Promise<MetadataStats | null>;
|
|
1756
|
+
/**
|
|
1757
|
+
* [#15037] Report only the names that ARE names.
|
|
1758
|
+
*
|
|
1759
|
+
* This read used to be `loadMany<{ name: string }>(type)` mapped straight to
|
|
1760
|
+
* `items.map(i => i.name)`. That type argument is an ASSERTION about bodies
|
|
1761
|
+
* that arrived over HTTP, and nothing checked it: a body with no top-level
|
|
1762
|
+
* `name` yielded `undefined`, which went into an array this signature
|
|
1763
|
+
* declares as `string[]` and reached consumers through
|
|
1764
|
+
* `MetadataManager.listNames()` — a runtime violation of a declared type,
|
|
1765
|
+
* not an untidy entry. A consumer that keys by it, lower-cases it, or feeds
|
|
1766
|
+
* it back to a by-name `load()` gets `undefined` where the type says it
|
|
1767
|
+
* cannot be.
|
|
1768
|
+
*
|
|
1769
|
+
* The guard is `DatabaseLoader.list()`'s, one file away: same cast-then-map
|
|
1770
|
+
* spelling, one `typeof` filter behind it. Silently dropping is the landed
|
|
1771
|
+
* direction, not a preference — `DatabaseLoader` drops rather than throws,
|
|
1772
|
+
* and `FilesystemLoader`'s narrowing carries a maintainer ruling (via the
|
|
1773
|
+
* director seat on #14486, 2026-09-02) that chose narrowing (A) over
|
|
1774
|
+
* refusing loudly (B), because a name in the list that the door answers
|
|
1775
|
+
* `null` for is the silent failure an author reads as their own typo. An
|
|
1776
|
+
* `undefined` here is the extreme form of that name.
|
|
1777
|
+
*
|
|
1778
|
+
* ⛔ NOT copied from the siblings: `MemoryLoader` answers with its store
|
|
1779
|
+
* keys, and #14205 ruled that identity is the key the store holds an item
|
|
1780
|
+
* under rather than `body.name`. This loader reads over HTTP and holds no
|
|
1781
|
+
* store key, so `body.name` is the only identity it has — the list is
|
|
1782
|
+
* narrowed to agree with the door instead. `loadMany()` is deliberately
|
|
1783
|
+
* untouched: it keys nothing, so a nameless body is still served there.
|
|
1784
|
+
*
|
|
1785
|
+
* The predicate is spelled as a type guard, and the mapped element type left
|
|
1786
|
+
* `unknown`, so `tsc` PROVES the declared `string[]` instead of a cast
|
|
1787
|
+
* asserting it — otherwise the compiler reads the filter as always-true and
|
|
1788
|
+
* a later reader deletes it as dead.
|
|
1789
|
+
*/
|
|
1665
1790
|
list(type: string): Promise<string[]>;
|
|
1666
1791
|
save(type: string, name: string, data: any, _options?: MetadataSaveOptions): Promise<MetadataSaveResult>;
|
|
1667
1792
|
}
|
|
@@ -1740,8 +1865,14 @@ interface DatabaseLoaderCacheOptions {
|
|
|
1740
1865
|
enabled?: boolean;
|
|
1741
1866
|
/** Max number of cached `(type, name)` entries. Default: `500`. */
|
|
1742
1867
|
maxSize?: number;
|
|
1743
|
-
/**
|
|
1744
|
-
|
|
1868
|
+
/**
|
|
1869
|
+
* TTL in milliseconds. Set to `0` to disable expiry. Default: `60_000`.
|
|
1870
|
+
*
|
|
1871
|
+
* Renamed from `ttl` (#14478) in lockstep with the spec key it mirrors,
|
|
1872
|
+
* `MetadataManagerConfig.cache.databaseLoader.ttlMs`: the unit now lives in
|
|
1873
|
+
* the name, not only in this comment.
|
|
1874
|
+
*/
|
|
1875
|
+
ttlMs?: number;
|
|
1745
1876
|
}
|
|
1746
1877
|
/**
|
|
1747
1878
|
* Configuration for the DatabaseLoader.
|
|
@@ -2034,6 +2165,99 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
2034
2165
|
delete(type: string, name: string): Promise<void>;
|
|
2035
2166
|
}
|
|
2036
2167
|
|
|
2168
|
+
/**
|
|
2169
|
+
* [#14921] The refusal a metadata source tree earns by naming one item twice.
|
|
2170
|
+
*
|
|
2171
|
+
* ## The invariant this restores
|
|
2172
|
+
*
|
|
2173
|
+
* *What is listed is what is loadable.* `FilesystemLoader` derives a metadata
|
|
2174
|
+
* name by stripping the extension from a flat file's basename, and resolves a
|
|
2175
|
+
* name back to a file under a FIXED extension precedence (`.json` → `.yaml` →
|
|
2176
|
+
* `.yml` → `.ts` → `.js`). Two files sharing a stem therefore produced one name
|
|
2177
|
+
* TWICE in `list()` while only the first-precedence file was reachable through
|
|
2178
|
+
* any name at all: the listed set and the addressable set stopped being the
|
|
2179
|
+
* same set, and `loadMany()` kept returning both bodies. The loser was
|
|
2180
|
+
* invisible — not missing, not reported, just never served.
|
|
2181
|
+
*
|
|
2182
|
+
* The failure is silent in the direction that matters for authoring, and the
|
|
2183
|
+
* trigger is a move authors (human and AI) make constantly: convert
|
|
2184
|
+
* `twin.json` to `twin.yaml` and leave the old file behind, or land one from
|
|
2185
|
+
* each of two packages. Today the JSON one is served forever with no
|
|
2186
|
+
* diagnostic anywhere, and `MetadataManager.admitLoaderItems()`'s documented
|
|
2187
|
+
* "keep the first and say nothing" absorbs the collision a second time.
|
|
2188
|
+
*
|
|
2189
|
+
* ## The ruling (maintainer, via the director seat on #14921, 2026-09-05)
|
|
2190
|
+
*
|
|
2191
|
+
* Option 1 of three: **refuse the ambiguous stem loudly at list time.** Two
|
|
2192
|
+
* files sharing a stem across the registered extensions is an AUTHORING ERROR,
|
|
2193
|
+
* reported with both paths named, never resolved by precedence. Not taken:
|
|
2194
|
+
* option 2 (keep the precedence and log at `warn` — with zero instances in any
|
|
2195
|
+
* measured tree, nobody reads that log, and the invariant stays broken) and
|
|
2196
|
+
* option 3 (make the extension part of the name for the non-first file — a
|
|
2197
|
+
* naming rule invented for an error state, grown into the contract).
|
|
2198
|
+
*
|
|
2199
|
+
* The narrowing is cheap for the reason the grade records: no measured
|
|
2200
|
+
* production or example tree carries two files with one stem, so no existing
|
|
2201
|
+
* tree goes red. It is a narrowing with almost no migration account.
|
|
2202
|
+
*
|
|
2203
|
+
* ## Why a brand and a predicate rather than bare `instanceof`
|
|
2204
|
+
*
|
|
2205
|
+
* `MetadataManager`'s plural reads catch per loader on purpose (#5108/#14423):
|
|
2206
|
+
* a storage outage must degrade to a short-but-served list rather than take the
|
|
2207
|
+
* whole enumeration down. This refusal is the opposite kind of fact — an
|
|
2208
|
+
* author's tree is malformed and no retry fixes it — so those seams have to
|
|
2209
|
+
* re-raise THIS error while still absorbing every other one. A predicate over
|
|
2210
|
+
* a `Symbol.for` brand is the discrimination that survives duplicate copies of
|
|
2211
|
+
* this module in a consumer's dependency graph, where `instanceof` does not.
|
|
2212
|
+
* Same shape, and for the same reason, as `@objectstack/core`'s
|
|
2213
|
+
* `isAuthzStoreUnavailableError`.
|
|
2214
|
+
*/
|
|
2215
|
+
/** ADR-0112 wire code for the refusal. */
|
|
2216
|
+
declare const AMBIGUOUS_METADATA_STEM_CODE: "AMBIGUOUS_METADATA_STEM";
|
|
2217
|
+
/**
|
|
2218
|
+
* HTTP status a transport should answer.
|
|
2219
|
+
*
|
|
2220
|
+
* 500, deliberately: the REQUEST is well formed and no caller can fix it by
|
|
2221
|
+
* sending something else — the deployment's own metadata source tree is
|
|
2222
|
+
* ambiguous. Not 503 (nothing is transient here; a retry answers identically
|
|
2223
|
+
* until a file is deleted or renamed) and not 4xx (the caller did nothing
|
|
2224
|
+
* wrong).
|
|
2225
|
+
*/
|
|
2226
|
+
declare const AMBIGUOUS_METADATA_STEM_STATUS: 500;
|
|
2227
|
+
declare const AMBIGUOUS_METADATA_STEM_BRAND: unique symbol;
|
|
2228
|
+
/**
|
|
2229
|
+
* Thrown when one metadata name is derived from more than one file among a
|
|
2230
|
+
* loader's REGISTERED extensions.
|
|
2231
|
+
*
|
|
2232
|
+
* The message names every colliding path and the metadata type, because those
|
|
2233
|
+
* are exactly the two things an author needs and neither is recoverable from
|
|
2234
|
+
* the name alone: a bare "duplicate `twin`" sends them looking through a tree
|
|
2235
|
+
* for something they already believe they deleted.
|
|
2236
|
+
*/
|
|
2237
|
+
declare class AmbiguousMetadataStemError extends Error {
|
|
2238
|
+
/** Brand — see the module doc on why this is not `instanceof`. */
|
|
2239
|
+
readonly [AMBIGUOUS_METADATA_STEM_BRAND]: true;
|
|
2240
|
+
/** ADR-0112 wire code. */
|
|
2241
|
+
readonly code: "AMBIGUOUS_METADATA_STEM";
|
|
2242
|
+
/** HTTP status a transport should answer. */
|
|
2243
|
+
readonly status: 500;
|
|
2244
|
+
/** The metadata type whose directory holds the collision (e.g. `object`). */
|
|
2245
|
+
readonly type: string;
|
|
2246
|
+
/** The one name both files derive to. */
|
|
2247
|
+
readonly stem: string;
|
|
2248
|
+
/** Every colliding file, absolute, sorted — never just the winner. */
|
|
2249
|
+
readonly paths: readonly string[];
|
|
2250
|
+
constructor(type: string, stem: string, paths: readonly string[]);
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* True when `err` is the ambiguous-stem refusal above.
|
|
2254
|
+
*
|
|
2255
|
+
* The predicate every catch-and-degrade seam uses to re-raise THIS one without
|
|
2256
|
+
* loosening its handling of anything else — a storage outage still degrades, an
|
|
2257
|
+
* author's malformed tree does not.
|
|
2258
|
+
*/
|
|
2259
|
+
declare function isAmbiguousMetadataStemError(err: unknown): err is AmbiguousMetadataStemError;
|
|
2260
|
+
|
|
2037
2261
|
/**
|
|
2038
2262
|
* Metadata History Utilities
|
|
2039
2263
|
*
|
|
@@ -2178,61 +2402,4 @@ declare class TypeScriptSerializer implements MetadataSerializer {
|
|
|
2178
2402
|
getFormat(): MetadataFormat;
|
|
2179
2403
|
}
|
|
2180
2404
|
|
|
2181
|
-
|
|
2182
|
-
* [#13913] Registry-free expansion of an aggregated `defineView` container
|
|
2183
|
-
* that reached `MetadataManager`'s OWN backing store.
|
|
2184
|
-
*
|
|
2185
|
-
* ---------------------------------------------------------------------------
|
|
2186
|
-
* Why this lives in `packages/metadata` and does NOT import the protocol's copy
|
|
2187
|
-
* ---------------------------------------------------------------------------
|
|
2188
|
-
* `packages/metadata-protocol` grew `expandRuntimeViewContainer` (#13407) for
|
|
2189
|
-
* `getMetaItems`' own inline expansion, and the obvious move would be to share
|
|
2190
|
-
* it. **The dependency edge forbids it**: `@objectstack/metadata-protocol`
|
|
2191
|
-
* declares `@objectstack/metadata` as a dependency, so an import in this
|
|
2192
|
-
* direction inverts an existing edge and closes a cycle. Promoting the
|
|
2193
|
-
* protocol's private method to a public export would not help either — it
|
|
2194
|
-
* would widen that package's surface *and* still be unreachable from here.
|
|
2195
|
-
*
|
|
2196
|
-
* The reusable substance is therefore taken from where it already is: the
|
|
2197
|
-
* canonical expansion primitives (`isAggregatedViewContainer`,
|
|
2198
|
-
* `expandViewContainer`) live in `@objectstack/spec`, one level BELOW both
|
|
2199
|
-
* packages, and this package already imports them (`plugin.ts`). Nothing is
|
|
2200
|
-
* duplicated except the ~6-line object-derivation chain — which is exactly the
|
|
2201
|
-
* part that has silently drifted three ways (the ObjectQL boot loop keys off
|
|
2202
|
-
* the registration name, `plugin.ts` walks two levels, and `protocol.ts` walks
|
|
2203
|
-
* four since #13407). {@link deriveViewContainerObject} is the one spelling of
|
|
2204
|
-
* it for this package, so the drift has a single place to be repaired rather
|
|
2205
|
-
* than a third private copy to fall behind.
|
|
2206
|
-
*
|
|
2207
|
-
* ---------------------------------------------------------------------------
|
|
2208
|
-
* Registry-free, on purpose
|
|
2209
|
-
* ---------------------------------------------------------------------------
|
|
2210
|
-
* Nothing here REGISTERS. `protocol.ts`'s own header records why its
|
|
2211
|
-
* registry-MUTATING path (`hydrateExpandedViewItems`) could not simply be
|
|
2212
|
-
* widened — it is gated off for every org-scoped row (ADR-0005: the registry
|
|
2213
|
-
* is shared by every org a kernel serves) — and why #13407's actual repair was
|
|
2214
|
-
* a separate, registry-free, per-request expansion. The same reasoning applies
|
|
2215
|
-
* one exit over: `MetadataManager`'s registry is process-wide too, so the
|
|
2216
|
-
* repair for {@link MetadataManager.getViewsByObject} is a pure function over
|
|
2217
|
-
* what that one read already holds.
|
|
2218
|
-
*/
|
|
2219
|
-
|
|
2220
|
-
/**
|
|
2221
|
-
* Which object an aggregated view container binds to.
|
|
2222
|
-
*
|
|
2223
|
-
* The container's OWN top-level `object` field — `ViewSchema.object`,
|
|
2224
|
-
* documented there as "how a stack-level `views: [...]` entry says which object
|
|
2225
|
-
* its views belong to; read by `getViewsByObject()` / `GET /meta/view?object=`"
|
|
2226
|
-
* — is the authorial, explicit signal and is consulted FIRST (#13407). The
|
|
2227
|
-
* three-deep fallback below it is kept unchanged for every container written
|
|
2228
|
-
* before that field was read here: `list.data.object`, then `form.data.object`,
|
|
2229
|
-
* then the row's own `name` — which is the bound object only by convention, and
|
|
2230
|
-
* is why a container that set the top-level field but not `list.data.object`
|
|
2231
|
-
* used to bind under the wrong key or not at all.
|
|
2232
|
-
*
|
|
2233
|
-
* Returns `undefined` when no binding can be derived; every caller treats that
|
|
2234
|
-
* as "no expansion" rather than an error.
|
|
2235
|
-
*/
|
|
2236
|
-
declare function deriveViewContainerObject(container: unknown): string | undefined;
|
|
2237
|
-
|
|
2238
|
-
export { DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataKeyedItem, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, deriveViewContainerObject, generateDiffSummary, generateSimpleDiff };
|
|
2405
|
+
export { AMBIGUOUS_METADATA_STEM_CODE, AMBIGUOUS_METADATA_STEM_STATUS, AmbiguousMetadataStemError, DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataKeyedItem, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, generateDiffSummary, generateSimpleDiff, isAmbiguousMetadataStemError };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { Logger, Plugin, PluginContext } from '@objectstack/core';
|
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { MetadataRepository } from '@objectstack/metadata-core';
|
|
11
11
|
export { HistoryOptions, MetaRef, MetadataEvent, MetadataItem, MetadataItemHeader, MetadataRepository, SysMetadataHistoryObject, SysMetadataObject, WatchFilter } from '@objectstack/metadata-core';
|
|
12
|
+
export { deriveViewContainerObject } from './view-container.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Metadata Serializer Interface
|
|
@@ -868,6 +869,30 @@ declare class MetadataManager implements IMetadataService {
|
|
|
868
869
|
exists(type: string, name: string): Promise<boolean>;
|
|
869
870
|
/**
|
|
870
871
|
* List all names of metadata items of a given type
|
|
872
|
+
*
|
|
873
|
+
* ## [#14423] One loader's fault does not take the whole enumeration down
|
|
874
|
+
*
|
|
875
|
+
* This loop used to be bare — `const result = await loader.list(type)` with
|
|
876
|
+
* no `try`, while the two sibling plural reads (`list()` via
|
|
877
|
+
* {@link admitLoaderItems}, and {@link loadMany}) have carried a per-loader
|
|
878
|
+
* `catch` since #5108. That asymmetry is the defect, independent of any one
|
|
879
|
+
* caller: the SAME storage outage was swallowed by one plural read and
|
|
880
|
+
* thrown out of the other, so which answer a caller got depended only on
|
|
881
|
+
* which method it happened to call. A caller reading both — the action
|
|
882
|
+
* governance audit is one — saw `loadMany` report a short-but-successful
|
|
883
|
+
* set and `listNames` throw, and had no way to tell that one fact was
|
|
884
|
+
* behind both.
|
|
885
|
+
*
|
|
886
|
+
* Same shape as `loadMany`'s, deliberately, down to the helpers: the outage
|
|
887
|
+
* is spoken once per loader through {@link reportLoaderReadFailure} and
|
|
888
|
+
* un-said through {@link reportLoaderReadRecovered}. ⛔ Not a third spelling
|
|
889
|
+
* for "a loader faulted" — a second vocabulary for one event is how the two
|
|
890
|
+
* reads drifted apart in the first place.
|
|
891
|
+
*
|
|
892
|
+
* The degradation is the same one `list()` documents and is graded the same
|
|
893
|
+
* way (AGENTS.md → "Degradation log levels"): the caller still gets an
|
|
894
|
+
* array, nothing 500s, and the set is quietly short — so it is reported at
|
|
895
|
+
* `error`, by the shared helper, rather than being re-graded here.
|
|
871
896
|
*/
|
|
872
897
|
listNames(type: string): Promise<string[]>;
|
|
873
898
|
/**
|
|
@@ -1181,6 +1206,72 @@ declare class MetadataManager implements IMetadataService {
|
|
|
1181
1206
|
* Aggregates results from all loaders.
|
|
1182
1207
|
*/
|
|
1183
1208
|
loadMany<T = any>(type: string, options?: MetadataLoadOptions): Promise<T[]>;
|
|
1209
|
+
/**
|
|
1210
|
+
* [#14423] {@link loadMany}, read under the identity the STORE holds each
|
|
1211
|
+
* item by — the keyed plural read, beside the unkeyed one.
|
|
1212
|
+
*
|
|
1213
|
+
* ## Why a second method and not a widened `loadMany`
|
|
1214
|
+
*
|
|
1215
|
+
* `loadMany` keys nothing: it returns bodies, and every consumer that needs
|
|
1216
|
+
* an identity reads `body.name` off them. #14205 already ruled what identity
|
|
1217
|
+
* IS — the key the store holds the item under (`register(type, name, data)`
|
|
1218
|
+
* takes it as the ARGUMENT, and a body is not required to name itself) — so
|
|
1219
|
+
* `body.name` is a guess that happens to be right for most items and drops
|
|
1220
|
+
* the rest ENTIRELY: an item whose body carries no `name` is served by
|
|
1221
|
+
* `load(type, name)` and is not nameable from `loadMany`'s answer at all.
|
|
1222
|
+
*
|
|
1223
|
+
* Widening `loadMany`'s return would fix that and break every consumer of a
|
|
1224
|
+
* published shape (the ones counted on this card all read `body.name` as the
|
|
1225
|
+
* identity). So this is additive: `loadMany`'s return shape is untouched,
|
|
1226
|
+
* and a caller that needs the key asks for the key.
|
|
1227
|
+
*
|
|
1228
|
+
* ## What it reads — the same population `loadMany` reads
|
|
1229
|
+
*
|
|
1230
|
+
* Loaders only, deliberately, so this is `loadMany` keyed and nothing more.
|
|
1231
|
+
* It is NOT `list()`/{@link listNames}, which also merge the in-memory
|
|
1232
|
+
* `register()` registry; a caller wanting that set has those. Reading the
|
|
1233
|
+
* loaders alone is also what makes this the enumerable twin of
|
|
1234
|
+
* {@link loadDiagnosed}, which walks the same loaders by name — that pairing
|
|
1235
|
+
* is the point on the audit side of #14423, where an enumeration and a
|
|
1236
|
+
* by-name read that disagree about a population make one subsystem accuse
|
|
1237
|
+
* another of a defect neither has.
|
|
1238
|
+
*
|
|
1239
|
+
* ## Delegate first, fall back second — and why that order is not a style
|
|
1240
|
+
*
|
|
1241
|
+
* Per loader: {@link MetadataLoader.loadManyKeyed} where the loader offers
|
|
1242
|
+
* one, else its `list()` + a per-name `load()`. Measured, on
|
|
1243
|
+
* `DatabaseLoader`: the keyed method shares `loadMany`'s single query
|
|
1244
|
+
* (`{find:1, findOne:0}` — zero extra cost), while enumerate-then-read-each
|
|
1245
|
+
* on that same loader is a real N+1 (`{find:1, findOne:5}` for five items).
|
|
1246
|
+
* The fallback exists for loaders that cannot produce keys at all
|
|
1247
|
+
* (`RemoteLoader`'s wire format carries bodies only), and it recovers the
|
|
1248
|
+
* nameless item the pre-#14205 `loadMany`-and-key-by-`body.name` fallback
|
|
1249
|
+
* drops — which is why it is `list()` + `load()` and not `loadMany()`.
|
|
1250
|
+
*
|
|
1251
|
+
* ## Failure posture
|
|
1252
|
+
*
|
|
1253
|
+
* Per-loader `try`/`catch`, the same seam and the same helpers as
|
|
1254
|
+
* {@link loadMany} and `list()` — one loader's outage does not take the
|
|
1255
|
+
* enumeration down, and it is reported once through
|
|
1256
|
+
* {@link reportLoaderReadFailure} rather than in a third vocabulary.
|
|
1257
|
+
* Earlier loaders win a key collision, mirroring `list()`.
|
|
1258
|
+
*/
|
|
1259
|
+
loadManyKeyed<T = any>(type: string, options?: MetadataLoadOptions): Promise<MetadataKeyedItem<T>[]>;
|
|
1260
|
+
/**
|
|
1261
|
+
* Merge ONE loader's answer for `type` into `items`, keyed by that loader's
|
|
1262
|
+
* own key for each item — {@link loadManyKeyed}'s per-loader body.
|
|
1263
|
+
*
|
|
1264
|
+
* Distinct from {@link admitLoaderItems} on exactly one axis, and that axis
|
|
1265
|
+
* is the whole of #14423: the fallback for a loader with no
|
|
1266
|
+
* `loadManyKeyed`. `admitLoaderItems` falls back to `loadMany` keyed by
|
|
1267
|
+
* `data.name` — the pre-#14205 behaviour, verbatim, which drops a nameless
|
|
1268
|
+
* body. Here the fallback is `list()` + a per-name `load()`, so a loader
|
|
1269
|
+
* that cannot enumerate keys and bodies together still answers with both.
|
|
1270
|
+
*
|
|
1271
|
+
* Read failures are NOT caught here — the caller owns that verdict, as in
|
|
1272
|
+
* {@link admitLoaderItems}.
|
|
1273
|
+
*/
|
|
1274
|
+
private admitKeyedLoaderItems;
|
|
1184
1275
|
/**
|
|
1185
1276
|
* Save metadata item to a loader
|
|
1186
1277
|
*/
|
|
@@ -1662,6 +1753,40 @@ declare class RemoteLoader implements MetadataLoader {
|
|
|
1662
1753
|
loadMany<T = any>(type: string, _options?: MetadataLoadOptions): Promise<T[]>;
|
|
1663
1754
|
exists(type: string, name: string): Promise<boolean>;
|
|
1664
1755
|
stat(type: string, name: string): Promise<MetadataStats | null>;
|
|
1756
|
+
/**
|
|
1757
|
+
* [#15037] Report only the names that ARE names.
|
|
1758
|
+
*
|
|
1759
|
+
* This read used to be `loadMany<{ name: string }>(type)` mapped straight to
|
|
1760
|
+
* `items.map(i => i.name)`. That type argument is an ASSERTION about bodies
|
|
1761
|
+
* that arrived over HTTP, and nothing checked it: a body with no top-level
|
|
1762
|
+
* `name` yielded `undefined`, which went into an array this signature
|
|
1763
|
+
* declares as `string[]` and reached consumers through
|
|
1764
|
+
* `MetadataManager.listNames()` — a runtime violation of a declared type,
|
|
1765
|
+
* not an untidy entry. A consumer that keys by it, lower-cases it, or feeds
|
|
1766
|
+
* it back to a by-name `load()` gets `undefined` where the type says it
|
|
1767
|
+
* cannot be.
|
|
1768
|
+
*
|
|
1769
|
+
* The guard is `DatabaseLoader.list()`'s, one file away: same cast-then-map
|
|
1770
|
+
* spelling, one `typeof` filter behind it. Silently dropping is the landed
|
|
1771
|
+
* direction, not a preference — `DatabaseLoader` drops rather than throws,
|
|
1772
|
+
* and `FilesystemLoader`'s narrowing carries a maintainer ruling (via the
|
|
1773
|
+
* director seat on #14486, 2026-09-02) that chose narrowing (A) over
|
|
1774
|
+
* refusing loudly (B), because a name in the list that the door answers
|
|
1775
|
+
* `null` for is the silent failure an author reads as their own typo. An
|
|
1776
|
+
* `undefined` here is the extreme form of that name.
|
|
1777
|
+
*
|
|
1778
|
+
* ⛔ NOT copied from the siblings: `MemoryLoader` answers with its store
|
|
1779
|
+
* keys, and #14205 ruled that identity is the key the store holds an item
|
|
1780
|
+
* under rather than `body.name`. This loader reads over HTTP and holds no
|
|
1781
|
+
* store key, so `body.name` is the only identity it has — the list is
|
|
1782
|
+
* narrowed to agree with the door instead. `loadMany()` is deliberately
|
|
1783
|
+
* untouched: it keys nothing, so a nameless body is still served there.
|
|
1784
|
+
*
|
|
1785
|
+
* The predicate is spelled as a type guard, and the mapped element type left
|
|
1786
|
+
* `unknown`, so `tsc` PROVES the declared `string[]` instead of a cast
|
|
1787
|
+
* asserting it — otherwise the compiler reads the filter as always-true and
|
|
1788
|
+
* a later reader deletes it as dead.
|
|
1789
|
+
*/
|
|
1665
1790
|
list(type: string): Promise<string[]>;
|
|
1666
1791
|
save(type: string, name: string, data: any, _options?: MetadataSaveOptions): Promise<MetadataSaveResult>;
|
|
1667
1792
|
}
|
|
@@ -1740,8 +1865,14 @@ interface DatabaseLoaderCacheOptions {
|
|
|
1740
1865
|
enabled?: boolean;
|
|
1741
1866
|
/** Max number of cached `(type, name)` entries. Default: `500`. */
|
|
1742
1867
|
maxSize?: number;
|
|
1743
|
-
/**
|
|
1744
|
-
|
|
1868
|
+
/**
|
|
1869
|
+
* TTL in milliseconds. Set to `0` to disable expiry. Default: `60_000`.
|
|
1870
|
+
*
|
|
1871
|
+
* Renamed from `ttl` (#14478) in lockstep with the spec key it mirrors,
|
|
1872
|
+
* `MetadataManagerConfig.cache.databaseLoader.ttlMs`: the unit now lives in
|
|
1873
|
+
* the name, not only in this comment.
|
|
1874
|
+
*/
|
|
1875
|
+
ttlMs?: number;
|
|
1745
1876
|
}
|
|
1746
1877
|
/**
|
|
1747
1878
|
* Configuration for the DatabaseLoader.
|
|
@@ -2034,6 +2165,99 @@ declare class DatabaseLoader implements MetadataLoader {
|
|
|
2034
2165
|
delete(type: string, name: string): Promise<void>;
|
|
2035
2166
|
}
|
|
2036
2167
|
|
|
2168
|
+
/**
|
|
2169
|
+
* [#14921] The refusal a metadata source tree earns by naming one item twice.
|
|
2170
|
+
*
|
|
2171
|
+
* ## The invariant this restores
|
|
2172
|
+
*
|
|
2173
|
+
* *What is listed is what is loadable.* `FilesystemLoader` derives a metadata
|
|
2174
|
+
* name by stripping the extension from a flat file's basename, and resolves a
|
|
2175
|
+
* name back to a file under a FIXED extension precedence (`.json` → `.yaml` →
|
|
2176
|
+
* `.yml` → `.ts` → `.js`). Two files sharing a stem therefore produced one name
|
|
2177
|
+
* TWICE in `list()` while only the first-precedence file was reachable through
|
|
2178
|
+
* any name at all: the listed set and the addressable set stopped being the
|
|
2179
|
+
* same set, and `loadMany()` kept returning both bodies. The loser was
|
|
2180
|
+
* invisible — not missing, not reported, just never served.
|
|
2181
|
+
*
|
|
2182
|
+
* The failure is silent in the direction that matters for authoring, and the
|
|
2183
|
+
* trigger is a move authors (human and AI) make constantly: convert
|
|
2184
|
+
* `twin.json` to `twin.yaml` and leave the old file behind, or land one from
|
|
2185
|
+
* each of two packages. Today the JSON one is served forever with no
|
|
2186
|
+
* diagnostic anywhere, and `MetadataManager.admitLoaderItems()`'s documented
|
|
2187
|
+
* "keep the first and say nothing" absorbs the collision a second time.
|
|
2188
|
+
*
|
|
2189
|
+
* ## The ruling (maintainer, via the director seat on #14921, 2026-09-05)
|
|
2190
|
+
*
|
|
2191
|
+
* Option 1 of three: **refuse the ambiguous stem loudly at list time.** Two
|
|
2192
|
+
* files sharing a stem across the registered extensions is an AUTHORING ERROR,
|
|
2193
|
+
* reported with both paths named, never resolved by precedence. Not taken:
|
|
2194
|
+
* option 2 (keep the precedence and log at `warn` — with zero instances in any
|
|
2195
|
+
* measured tree, nobody reads that log, and the invariant stays broken) and
|
|
2196
|
+
* option 3 (make the extension part of the name for the non-first file — a
|
|
2197
|
+
* naming rule invented for an error state, grown into the contract).
|
|
2198
|
+
*
|
|
2199
|
+
* The narrowing is cheap for the reason the grade records: no measured
|
|
2200
|
+
* production or example tree carries two files with one stem, so no existing
|
|
2201
|
+
* tree goes red. It is a narrowing with almost no migration account.
|
|
2202
|
+
*
|
|
2203
|
+
* ## Why a brand and a predicate rather than bare `instanceof`
|
|
2204
|
+
*
|
|
2205
|
+
* `MetadataManager`'s plural reads catch per loader on purpose (#5108/#14423):
|
|
2206
|
+
* a storage outage must degrade to a short-but-served list rather than take the
|
|
2207
|
+
* whole enumeration down. This refusal is the opposite kind of fact — an
|
|
2208
|
+
* author's tree is malformed and no retry fixes it — so those seams have to
|
|
2209
|
+
* re-raise THIS error while still absorbing every other one. A predicate over
|
|
2210
|
+
* a `Symbol.for` brand is the discrimination that survives duplicate copies of
|
|
2211
|
+
* this module in a consumer's dependency graph, where `instanceof` does not.
|
|
2212
|
+
* Same shape, and for the same reason, as `@objectstack/core`'s
|
|
2213
|
+
* `isAuthzStoreUnavailableError`.
|
|
2214
|
+
*/
|
|
2215
|
+
/** ADR-0112 wire code for the refusal. */
|
|
2216
|
+
declare const AMBIGUOUS_METADATA_STEM_CODE: "AMBIGUOUS_METADATA_STEM";
|
|
2217
|
+
/**
|
|
2218
|
+
* HTTP status a transport should answer.
|
|
2219
|
+
*
|
|
2220
|
+
* 500, deliberately: the REQUEST is well formed and no caller can fix it by
|
|
2221
|
+
* sending something else — the deployment's own metadata source tree is
|
|
2222
|
+
* ambiguous. Not 503 (nothing is transient here; a retry answers identically
|
|
2223
|
+
* until a file is deleted or renamed) and not 4xx (the caller did nothing
|
|
2224
|
+
* wrong).
|
|
2225
|
+
*/
|
|
2226
|
+
declare const AMBIGUOUS_METADATA_STEM_STATUS: 500;
|
|
2227
|
+
declare const AMBIGUOUS_METADATA_STEM_BRAND: unique symbol;
|
|
2228
|
+
/**
|
|
2229
|
+
* Thrown when one metadata name is derived from more than one file among a
|
|
2230
|
+
* loader's REGISTERED extensions.
|
|
2231
|
+
*
|
|
2232
|
+
* The message names every colliding path and the metadata type, because those
|
|
2233
|
+
* are exactly the two things an author needs and neither is recoverable from
|
|
2234
|
+
* the name alone: a bare "duplicate `twin`" sends them looking through a tree
|
|
2235
|
+
* for something they already believe they deleted.
|
|
2236
|
+
*/
|
|
2237
|
+
declare class AmbiguousMetadataStemError extends Error {
|
|
2238
|
+
/** Brand — see the module doc on why this is not `instanceof`. */
|
|
2239
|
+
readonly [AMBIGUOUS_METADATA_STEM_BRAND]: true;
|
|
2240
|
+
/** ADR-0112 wire code. */
|
|
2241
|
+
readonly code: "AMBIGUOUS_METADATA_STEM";
|
|
2242
|
+
/** HTTP status a transport should answer. */
|
|
2243
|
+
readonly status: 500;
|
|
2244
|
+
/** The metadata type whose directory holds the collision (e.g. `object`). */
|
|
2245
|
+
readonly type: string;
|
|
2246
|
+
/** The one name both files derive to. */
|
|
2247
|
+
readonly stem: string;
|
|
2248
|
+
/** Every colliding file, absolute, sorted — never just the winner. */
|
|
2249
|
+
readonly paths: readonly string[];
|
|
2250
|
+
constructor(type: string, stem: string, paths: readonly string[]);
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* True when `err` is the ambiguous-stem refusal above.
|
|
2254
|
+
*
|
|
2255
|
+
* The predicate every catch-and-degrade seam uses to re-raise THIS one without
|
|
2256
|
+
* loosening its handling of anything else — a storage outage still degrades, an
|
|
2257
|
+
* author's malformed tree does not.
|
|
2258
|
+
*/
|
|
2259
|
+
declare function isAmbiguousMetadataStemError(err: unknown): err is AmbiguousMetadataStemError;
|
|
2260
|
+
|
|
2037
2261
|
/**
|
|
2038
2262
|
* Metadata History Utilities
|
|
2039
2263
|
*
|
|
@@ -2178,61 +2402,4 @@ declare class TypeScriptSerializer implements MetadataSerializer {
|
|
|
2178
2402
|
getFormat(): MetadataFormat;
|
|
2179
2403
|
}
|
|
2180
2404
|
|
|
2181
|
-
|
|
2182
|
-
* [#13913] Registry-free expansion of an aggregated `defineView` container
|
|
2183
|
-
* that reached `MetadataManager`'s OWN backing store.
|
|
2184
|
-
*
|
|
2185
|
-
* ---------------------------------------------------------------------------
|
|
2186
|
-
* Why this lives in `packages/metadata` and does NOT import the protocol's copy
|
|
2187
|
-
* ---------------------------------------------------------------------------
|
|
2188
|
-
* `packages/metadata-protocol` grew `expandRuntimeViewContainer` (#13407) for
|
|
2189
|
-
* `getMetaItems`' own inline expansion, and the obvious move would be to share
|
|
2190
|
-
* it. **The dependency edge forbids it**: `@objectstack/metadata-protocol`
|
|
2191
|
-
* declares `@objectstack/metadata` as a dependency, so an import in this
|
|
2192
|
-
* direction inverts an existing edge and closes a cycle. Promoting the
|
|
2193
|
-
* protocol's private method to a public export would not help either — it
|
|
2194
|
-
* would widen that package's surface *and* still be unreachable from here.
|
|
2195
|
-
*
|
|
2196
|
-
* The reusable substance is therefore taken from where it already is: the
|
|
2197
|
-
* canonical expansion primitives (`isAggregatedViewContainer`,
|
|
2198
|
-
* `expandViewContainer`) live in `@objectstack/spec`, one level BELOW both
|
|
2199
|
-
* packages, and this package already imports them (`plugin.ts`). Nothing is
|
|
2200
|
-
* duplicated except the ~6-line object-derivation chain — which is exactly the
|
|
2201
|
-
* part that has silently drifted three ways (the ObjectQL boot loop keys off
|
|
2202
|
-
* the registration name, `plugin.ts` walks two levels, and `protocol.ts` walks
|
|
2203
|
-
* four since #13407). {@link deriveViewContainerObject} is the one spelling of
|
|
2204
|
-
* it for this package, so the drift has a single place to be repaired rather
|
|
2205
|
-
* than a third private copy to fall behind.
|
|
2206
|
-
*
|
|
2207
|
-
* ---------------------------------------------------------------------------
|
|
2208
|
-
* Registry-free, on purpose
|
|
2209
|
-
* ---------------------------------------------------------------------------
|
|
2210
|
-
* Nothing here REGISTERS. `protocol.ts`'s own header records why its
|
|
2211
|
-
* registry-MUTATING path (`hydrateExpandedViewItems`) could not simply be
|
|
2212
|
-
* widened — it is gated off for every org-scoped row (ADR-0005: the registry
|
|
2213
|
-
* is shared by every org a kernel serves) — and why #13407's actual repair was
|
|
2214
|
-
* a separate, registry-free, per-request expansion. The same reasoning applies
|
|
2215
|
-
* one exit over: `MetadataManager`'s registry is process-wide too, so the
|
|
2216
|
-
* repair for {@link MetadataManager.getViewsByObject} is a pure function over
|
|
2217
|
-
* what that one read already holds.
|
|
2218
|
-
*/
|
|
2219
|
-
|
|
2220
|
-
/**
|
|
2221
|
-
* Which object an aggregated view container binds to.
|
|
2222
|
-
*
|
|
2223
|
-
* The container's OWN top-level `object` field — `ViewSchema.object`,
|
|
2224
|
-
* documented there as "how a stack-level `views: [...]` entry says which object
|
|
2225
|
-
* its views belong to; read by `getViewsByObject()` / `GET /meta/view?object=`"
|
|
2226
|
-
* — is the authorial, explicit signal and is consulted FIRST (#13407). The
|
|
2227
|
-
* three-deep fallback below it is kept unchanged for every container written
|
|
2228
|
-
* before that field was read here: `list.data.object`, then `form.data.object`,
|
|
2229
|
-
* then the row's own `name` — which is the bound object only by convention, and
|
|
2230
|
-
* is why a container that set the top-level field but not `list.data.object`
|
|
2231
|
-
* used to bind under the wrong key or not at all.
|
|
2232
|
-
*
|
|
2233
|
-
* Returns `undefined` when no binding can be derived; every caller treats that
|
|
2234
|
-
* as "no expansion" rather than an error.
|
|
2235
|
-
*/
|
|
2236
|
-
declare function deriveViewContainerObject(container: unknown): string | undefined;
|
|
2237
|
-
|
|
2238
|
-
export { DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataKeyedItem, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, deriveViewContainerObject, generateDiffSummary, generateSimpleDiff };
|
|
2405
|
+
export { AMBIGUOUS_METADATA_STEM_CODE, AMBIGUOUS_METADATA_STEM_STATUS, AmbiguousMetadataStemError, DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataKeyedItem, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, generateDiffSummary, generateSimpleDiff, isAmbiguousMetadataStemError };
|