@adhd/backlog 1.0.3 → 1.0.5
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 +26 -0
- package/api.ir.json +1 -1
- package/envelope.d.ts +1 -1
- package/index.js +51 -51
- package/index.mjs +4255 -4095
- package/package.json +1 -1
- package/query/query.d.ts +12 -9
- package/query/resolve.d.ts +27 -0
- package/store/catalog-invariant-guard.d.ts +10 -4
- package/write/catalog.d.ts +32 -13
- package/write/errors.d.ts +74 -2
- package/write/tx.d.ts +23 -1
- package/write/uid-prefix.d.ts +78 -0
package/package.json
CHANGED
package/query/query.d.ts
CHANGED
|
@@ -48,15 +48,18 @@ export interface IQueryStoreHandle {
|
|
|
48
48
|
*/
|
|
49
49
|
readonly assertVocabulary?: () => Promise<void>;
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
51
|
+
* Status/priority catalog invariant check (`store/catalog-invariant-guard.ts`),
|
|
52
|
+
* exposed OPT-IN — deliberately NOT consulted by `queryIssuesWithMeta`, and
|
|
53
|
+
* therefore NEVER on the ordinary read path. A drifted catalog (an unflagged
|
|
54
|
+
* reserved terminal status, or two same-kind rows sharing a case fold) is a
|
|
55
|
+
* bounded data problem: the read path serves the store regardless, and the
|
|
56
|
+
* drift is surfaced as a NAMED, non-zero check by the `store-check` CLI verb.
|
|
57
|
+
* `api.ts`'s `queryHandle` still wires it for every real host, so a caller
|
|
58
|
+
* that WANTS to assert explicitly can call `handle.assertCatalogInvariants?.()`
|
|
59
|
+
* — but no read ever does so implicitly. (Earlier this was awaited inside
|
|
60
|
+
* `queryIssuesWithMeta`; that abort turned one drifted row into a total read
|
|
61
|
+
* outage and was removed. The write path's per-verb abort was removed the
|
|
62
|
+
* same way, so neither ordinary path is gated.)
|
|
60
63
|
*/
|
|
61
64
|
readonly assertCatalogInvariants?: () => Promise<void>;
|
|
62
65
|
}
|
package/query/resolve.d.ts
CHANGED
|
@@ -9,12 +9,39 @@ export interface IResolvedRef {
|
|
|
9
9
|
name: string;
|
|
10
10
|
record: NodeRecord;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolve `ref` to exactly one LIVE node, by exact uid or by a UNIQUE uid
|
|
14
|
+
* prefix. Exact match is the fast path and always wins.
|
|
15
|
+
*
|
|
16
|
+
* Errors: `AmbiguousReferenceError` when a prefix matches ≥2 live nodes (the
|
|
17
|
+
* candidates are carried on the error and named in its message);
|
|
18
|
+
* `IssueNotFoundError` (kind `issue`) / `CatalogNotFoundError` (any other
|
|
19
|
+
* kind) when an exact uid or prefix matches nothing; `InvalidArgumentError`
|
|
20
|
+
* when `ref` is a uid attempt shorter than the minimum prefix length.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveUidPrefix(graph: GraphBackend, ref: string, opts?: {
|
|
23
|
+
expectedKind?: string;
|
|
24
|
+
}): Promise<NodeRecord>;
|
|
25
|
+
/**
|
|
26
|
+
* Like {@link resolveUidPrefix}, but `null` instead of a not-found error when
|
|
27
|
+
* nothing matches — for callers that treat a uid miss as "fall through to a
|
|
28
|
+
* name lookup." An AMBIGUOUS prefix still throws (never silently picks one),
|
|
29
|
+
* and a too-short uid attempt returns `null` so a genuine short business name
|
|
30
|
+
* is not swallowed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function tryResolveUidPrefix(graph: GraphBackend, ref: string, opts?: {
|
|
33
|
+
expectedKind?: string;
|
|
34
|
+
}): Promise<NodeRecord | null>;
|
|
12
35
|
/**
|
|
13
36
|
* Resolve `uid` → the live `issue` node, or throw {@link IssueNotFoundError}
|
|
14
37
|
* (SPEC.md §6.1: "a `uid` with no matching live node throws
|
|
15
38
|
* `IssueNotFoundError(uid)`"). This is the READ-PATH counterpart of
|
|
16
39
|
* `write/tx.ts`'s `getNodeByUidTx` — safe to call standalone because it is
|
|
17
40
|
* not composing a check-then-act write around the result.
|
|
41
|
+
*
|
|
42
|
+
* Accepts an exact uid or a UNIQUE uid prefix (see {@link resolveUidPrefix});
|
|
43
|
+
* a superseded node — whether reached by exact uid or prefix — throws the same
|
|
44
|
+
* {@link StaleSupersedeError} pointing at the chain head.
|
|
18
45
|
*/
|
|
19
46
|
export declare function resolveIssueByUid(graph: GraphBackend, uid: string): Promise<NodeRecord>;
|
|
20
47
|
/**
|
|
@@ -53,10 +53,16 @@ export declare function inspectCatalogInvariants(adapter: StoreAdapter): Promise
|
|
|
53
53
|
* Throws {@link CatalogInvariantError}, naming every offending row and the
|
|
54
54
|
* repair that resolves it, otherwise.
|
|
55
55
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
56
|
+
* WHERE IT RUNS: the `store-check` CLI verb — a NAMED, non-zero check an
|
|
57
|
+
* operator runs deliberately — plus any caller that asserts deliberately via
|
|
58
|
+
* `IQueryStoreHandle.assertCatalogInvariants`. It is deliberately NOT run on
|
|
59
|
+
* the ordinary read path (`queryIssuesWithMeta`), on the write path (`api.ts`'s
|
|
60
|
+
* `writeHandle`), or at store open (`openGraphBacklogStore`): a violation must
|
|
61
|
+
* be detected loudly and REPORTED, never allowed to turn every read — or every
|
|
62
|
+
* unrelated write — into an outage.
|
|
63
|
+
*
|
|
64
|
+
* BOUNDED BY CONSTRUCTION: it must not scan the issue graph per call. It reads
|
|
65
|
+
* only the (small) status/priority catalog — see
|
|
60
66
|
* {@link inspectCatalogInvariants}.
|
|
61
67
|
*/
|
|
62
68
|
export declare function assertCatalogInvariants(adapter: StoreAdapter): Promise<void>;
|
package/write/catalog.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { IComponentSummary, ILocationSummary, ILocationType, IProjectSummary } from '../query/types.js';
|
|
2
2
|
import { IEdgeKindRule, IWriteStoreHandle } from './tx.js';
|
|
3
|
+
import { isUidShaped } from './uid-prefix.js';
|
|
3
4
|
import { AdapterTransaction } from '@adhd/sox-store-adapter';
|
|
4
5
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Disambiguation by SHAPE, not a second field (§6.1): a 36-character
|
|
8
|
+
* version-4-UUID-formatted string is a `uid`; anything else is a `name`. The
|
|
9
|
+
* single definition lives in `write/uid-prefix.ts` (which also owns prefix
|
|
10
|
+
* classification); re-exported here so every existing `isUidShaped` importer
|
|
11
|
+
* keeps its path unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export { isUidShaped };
|
|
7
14
|
export interface IResolvedCatalogRow {
|
|
8
15
|
rowid: number;
|
|
9
16
|
uid: string;
|
|
@@ -73,21 +80,32 @@ export interface IMintOrResolveInput {
|
|
|
73
80
|
* that reads as non-terminal — the drift `catalog-repair.ts` exists to clean
|
|
74
81
|
* up is not regenerated on the next mint.
|
|
75
82
|
*
|
|
76
|
-
* Membership is
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
+
* Membership is decided on the FOLDED name ({@link catalogNameFold}) — the
|
|
84
|
+
* same Unicode fold the duplicate guard and the case-fragment repair use, never
|
|
85
|
+
* SQL `lower()`/`NOCASE`, which fold only ASCII `A`–`Z`. The spellings below
|
|
86
|
+
* are the canonical catalog rows, but the PREDICATE folds, so `Closed`,
|
|
87
|
+
* `fixed`, `resolved` and `done` all count as reserved. This matters because a
|
|
88
|
+
* first-ever lowercase `fixed`/`resolved`/`done` has no uppercase row to
|
|
89
|
+
* collide against; under an exact-case predicate it would seed
|
|
90
|
+
* `terminal:false` — a closed status the read layer returns as open (backlog
|
|
91
|
+
* b4525bc3 / d7ec2c50). There is exactly ONE such table in the codebase
|
|
92
|
+
* (`catalog-repair.ts` re-exports this set rather than declaring its own).
|
|
83
93
|
*/
|
|
84
94
|
export declare const RESERVED_TERMINAL_STATUS_NAMES: ReadonlySet<string>;
|
|
85
|
-
/** Whether `name` is
|
|
95
|
+
/** Whether `name` is one of {@link RESERVED_TERMINAL_STATUS_NAMES} under the case fold — so any spelling of the reserved terminal vocabulary (`closed`/`Closed`/`CLOSED`, `fixed`/`FIXED`) counts. */
|
|
86
96
|
export declare function isReservedTerminalStatusName(name: string): boolean;
|
|
87
97
|
/**
|
|
88
98
|
* The flat-catalog find-then-create (§1, §4c, §6.1): `kind`/`status`/
|
|
89
99
|
* `priority`/`agent` are mintable on an unresolved NAME; a uid-shaped `ref`
|
|
90
100
|
* that does not resolve instead throws — minting NEVER applies to a uid.
|
|
101
|
+
*
|
|
102
|
+
* A NAME that case-folds to an EXISTING LIVE row of the same kind but is
|
|
103
|
+
* spelled differently (`IN_PROGRESS` when `in_progress` is live) is REFUSED
|
|
104
|
+
* with {@link CaseVariantNameError} — never fold-resolved onto the existing
|
|
105
|
+
* row, never minted as a twin. Identity is exact-case (`WHERE name = ?`), so a
|
|
106
|
+
* variant is neither the same row nor a genuinely new name; it is ambiguous,
|
|
107
|
+
* and the ambiguity is stopped here rather than allowed to grow a duplicate
|
|
108
|
+
* the catalog-invariant guard would then abort every read over.
|
|
91
109
|
*/
|
|
92
110
|
export declare function mintOrResolveCatalogTx(tx: AdapterTransaction, input: IMintOrResolveInput): Promise<IResolvedCatalogRow>;
|
|
93
111
|
/**
|
|
@@ -108,9 +126,10 @@ export declare function mintOrResolveCatalogTx(tx: AdapterTransaction, input: IM
|
|
|
108
126
|
* exact `(kind,name)` lookup inside {@link mintOrResolveCatalogTx} finds the
|
|
109
127
|
* seeded row on every subsequent call, so reseeding never duplicates — the
|
|
110
128
|
* store converges to one live `status` row per name. A case-variant name
|
|
111
|
-
* (`Closed` vs a seeded `closed`) is
|
|
112
|
-
*
|
|
113
|
-
*
|
|
129
|
+
* (`Closed` vs a seeded `closed`) is REFUSED by {@link mintOrResolveCatalogTx}
|
|
130
|
+
* with {@link CaseVariantNameError} rather than folded onto the existing row or
|
|
131
|
+
* minted as a twin — the same exact-case stop every other flat catalog takes
|
|
132
|
+
* (see that function's doc comment).
|
|
114
133
|
*
|
|
115
134
|
* A uid-shaped `ref` that does not resolve still throws
|
|
116
135
|
* `CatalogNotFoundError` — minting never applies to a uid (§6.1).
|
package/write/errors.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* All named classes here are `E_VALIDATION`- or `E_CONSTRAINT`-class
|
|
19
19
|
* members of the same union (SPEC.md §4c, "Two failure-signaling shapes,
|
|
20
20
|
* reconciled into one contract") — `IssueNotFoundError`, `InvalidArgumentError`,
|
|
21
|
-
* `CatalogNotFoundError`, `ClaimHeldError`, `SingleValuedRelationConflictError`,
|
|
21
|
+
* `CatalogNotFoundError`, `CaseVariantNameError`, `ClaimHeldError`, `SingleValuedRelationConflictError`,
|
|
22
22
|
* `NoteRequiredError`, `CitationRequiredError`, `CitationUnverifiableError` are
|
|
23
23
|
* `E_VALIDATION` (never retryable — thrown before any driver call runs);
|
|
24
24
|
* `StaleSupersedeError` is the one deliberate `E_CONSTRAINT` this spec's own
|
|
@@ -162,6 +162,71 @@ export declare class CatalogNotFoundError extends BacklogWriteError {
|
|
|
162
162
|
readonly retryable = false;
|
|
163
163
|
constructor(catalogKind: string, ref: string);
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* A uid reference matched MORE THAN ONE live node by prefix. Carries the
|
|
167
|
+
* candidate set (`uid` + `kind` + `name`) as structured data so a caller can
|
|
168
|
+
* disambiguate and re-run, and renders every candidate into the message.
|
|
169
|
+
*
|
|
170
|
+
* The resolver NEVER auto-selects among the candidates — silently returning
|
|
171
|
+
* the wrong item is strictly worse than refusing. Distinct from
|
|
172
|
+
* `IssueNotFoundError` (an exact uid that is genuinely absent) so a caller can
|
|
173
|
+
* branch structurally: an ambiguous reference is actionable (re-run with a
|
|
174
|
+
* longer prefix or the full uid), a missing one is not.
|
|
175
|
+
*
|
|
176
|
+
* `E_VALIDATION`, never retryable — retrying the identical short reference
|
|
177
|
+
* resolves to the identical candidate set.
|
|
178
|
+
*/
|
|
179
|
+
export declare class AmbiguousReferenceError extends BacklogWriteError {
|
|
180
|
+
readonly ref: string;
|
|
181
|
+
readonly candidates: ReadonlyArray<{
|
|
182
|
+
uid: string;
|
|
183
|
+
kind: string;
|
|
184
|
+
name: string;
|
|
185
|
+
}>;
|
|
186
|
+
readonly code: "E_VALIDATION";
|
|
187
|
+
readonly retryable = false;
|
|
188
|
+
constructor(ref: string, candidates: ReadonlyArray<{
|
|
189
|
+
uid: string;
|
|
190
|
+
kind: string;
|
|
191
|
+
name: string;
|
|
192
|
+
}>);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* A flat-catalog write named a token that differs from an EXISTING LIVE row of
|
|
196
|
+
* the SAME kind only by letter case (`IN_PROGRESS` vs `in_progress`, `high` vs
|
|
197
|
+
* `HIGH`). Catalog identity is EXACT-case (`DATA_MODEL.md:70-78`; the
|
|
198
|
+
* `(kind, name)` resolve in `write/catalog.ts` is `WHERE name = ?`), so the
|
|
199
|
+
* write path REFUSES the variant rather than fold-resolving it onto the
|
|
200
|
+
* existing row or minting a twin. Accepting it would grow a second live row
|
|
201
|
+
* that case-folds to the first — the case-fragment defect the uniqueness guard
|
|
202
|
+
* (`store/catalog-invariant-guard.ts`) exists to catch, which is exactly how
|
|
203
|
+
* the store wedged (BUG: a mixed-case write minted a twin, then the guard
|
|
204
|
+
* refused every read).
|
|
205
|
+
*
|
|
206
|
+
* The message names BOTH spellings and tells the caller which one to use: the
|
|
207
|
+
* one already live. The ambiguity is stopped here, at the single write call
|
|
208
|
+
* site, instead of aborting every read.
|
|
209
|
+
*
|
|
210
|
+
* `E_VALIDATION`, never retryable — this is a before-any-mint decision, so
|
|
211
|
+
* retrying the identical payload fails identically.
|
|
212
|
+
*/
|
|
213
|
+
export declare class CaseVariantNameError extends BacklogWriteError {
|
|
214
|
+
/** The flat-catalog kind (`status`, `priority`, `kind`, `agent`). */
|
|
215
|
+
readonly catalogKind: string;
|
|
216
|
+
/** The name of the row ALREADY LIVE for this kind — the spelling to use. */
|
|
217
|
+
readonly canonicalName: string;
|
|
218
|
+
/** The differing name the caller supplied — the spelling that was refused. */
|
|
219
|
+
readonly offendingName: string;
|
|
220
|
+
readonly code: "E_VALIDATION";
|
|
221
|
+
readonly retryable = false;
|
|
222
|
+
constructor(
|
|
223
|
+
/** The flat-catalog kind (`status`, `priority`, `kind`, `agent`). */
|
|
224
|
+
catalogKind: string,
|
|
225
|
+
/** The name of the row ALREADY LIVE for this kind — the spelling to use. */
|
|
226
|
+
canonicalName: string,
|
|
227
|
+
/** The differing name the caller supplied — the spelling that was refused. */
|
|
228
|
+
offendingName: string);
|
|
229
|
+
}
|
|
165
230
|
/** A caller-supplied argument is missing, blank, or fails a project-declared invariant. */
|
|
166
231
|
export declare class InvalidArgumentError extends BacklogWriteError {
|
|
167
232
|
readonly field: string;
|
|
@@ -172,9 +237,16 @@ export declare class InvalidArgumentError extends BacklogWriteError {
|
|
|
172
237
|
/** No live `issue` node carries the given `uid` (SPEC.md §6.1). */
|
|
173
238
|
export declare class IssueNotFoundError extends BacklogWriteError {
|
|
174
239
|
readonly uid: string;
|
|
240
|
+
readonly asPrefix: boolean;
|
|
175
241
|
readonly code: "E_VALIDATION";
|
|
176
242
|
readonly retryable = false;
|
|
177
|
-
|
|
243
|
+
/**
|
|
244
|
+
* @param uid The uid (or uid prefix) the caller passed in.
|
|
245
|
+
* @param asPrefix Set by the prefix resolver when `uid` was a short prefix
|
|
246
|
+
* rather than a full uid, so the message says "no item matches" instead of
|
|
247
|
+
* implying the caller passed a complete uid that is simply absent.
|
|
248
|
+
*/
|
|
249
|
+
constructor(uid: string, asPrefix?: boolean);
|
|
178
250
|
}
|
|
179
251
|
/**
|
|
180
252
|
* `claim` (§6.3.5): the lease is held by someone else, not yet stale, and the
|
package/write/tx.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INodeReadExecutor } from './uid-prefix.js';
|
|
1
2
|
import { BacklogWriteError } from './errors.js';
|
|
2
3
|
import { TypePolicy } from '@adhd/sox-graph-store';
|
|
3
4
|
import { AdapterTransaction, StoreAdapter } from '@adhd/sox-store-adapter';
|
|
@@ -98,7 +99,28 @@ export declare function canonicalJSONStringify(value: Record<string, unknown>):
|
|
|
98
99
|
* issued against `tx` instead of the bare adapter — never a `getNodeByUid`
|
|
99
100
|
* call itself, which always runs against `this.adapter` (§4c).
|
|
100
101
|
*/
|
|
101
|
-
export declare function getNodeByUidTx(tx:
|
|
102
|
+
export declare function getNodeByUidTx(tx: INodeReadExecutor, uid: string): Promise<ITxNodeRow | null>;
|
|
103
|
+
/**
|
|
104
|
+
* uid reference → LIVE `issue`/catalog node row, inside a tx, accepting a
|
|
105
|
+
* UNIQUE uid PREFIX as well as an exact uid. This is the single write-side
|
|
106
|
+
* funnel every issue verb ({@link resolveLiveIssueTx}) and catalog
|
|
107
|
+
* uid-resolution (`catalog.ts`'s `resolveByUidTx`, hence `rm-location`) goes
|
|
108
|
+
* through, so the prefix contract is defined once rather than per verb.
|
|
109
|
+
*
|
|
110
|
+
* Exact match is the fast path and keeps winning: a full uid is looked up
|
|
111
|
+
* directly, and only when that misses is a prefix scan attempted. A prefix
|
|
112
|
+
* below `MIN_UID_PREFIX_LENGTH` throws an "too short" argument error; a prefix
|
|
113
|
+
* that matches zero rows throws the kind-appropriate not-found; a prefix that
|
|
114
|
+
* matches two or more throws {@link AmbiguousReferenceError} naming every
|
|
115
|
+
* candidate — never an arbitrary pick.
|
|
116
|
+
*
|
|
117
|
+
* @throws IssueNotFoundError (kind `issue`) / CatalogNotFoundError (any other
|
|
118
|
+
* kind) when nothing live matches.
|
|
119
|
+
* @throws AmbiguousReferenceError when a prefix matches more than one live row.
|
|
120
|
+
*/
|
|
121
|
+
export declare function resolveUidPrefixTx(exec: INodeReadExecutor, ref: string, opts?: {
|
|
122
|
+
expectedKind?: string;
|
|
123
|
+
}): Promise<ITxNodeRow>;
|
|
102
124
|
/**
|
|
103
125
|
* Resolves `uid` to the CURRENT, live `issue` row, or throws.
|
|
104
126
|
*
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { InvalidArgumentError } from './errors.js';
|
|
2
|
+
|
|
3
|
+
/** Whether `ref` is an exact, full uid (resolve-or-throw), never a business `name` and never a prefix. */
|
|
4
|
+
export declare function isUidShaped(ref: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* The shortest uid prefix this resolver will accept. Eight hex characters —
|
|
7
|
+
* the first UUID block, the human-copyable unit, and 32 bits of address space;
|
|
8
|
+
* see this file's header for the full justification.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MIN_UID_PREFIX_LENGTH = 8;
|
|
11
|
+
/**
|
|
12
|
+
* Whether `ref` is a proper prefix (length `>=` {@link MIN_UID_PREFIX_LENGTH},
|
|
13
|
+
* `< 36`) of some canonical uid string — hex characters with hyphens only at
|
|
14
|
+
* the canonical boundary positions.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isUidPrefixShaped(ref: string): boolean;
|
|
17
|
+
/** How a caller-supplied reference reads: an exact uid, a uid prefix, a too-short uid attempt, or not uid-shaped at all. */
|
|
18
|
+
export type UidRefKind = 'exact' | 'prefix' | 'too-short' | 'not-uid';
|
|
19
|
+
/**
|
|
20
|
+
* Classify `ref` for uid resolution. `too-short` is a uid ATTEMPT (hex/hyphen
|
|
21
|
+
* only) below {@link MIN_UID_PREFIX_LENGTH} — a strict uid context rejects it
|
|
22
|
+
* as too short, while a uid-or-name context falls back to a name lookup, so a
|
|
23
|
+
* genuine 6-character value that is itself a valid name is never swallowed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function classifyUidRef(ref: string): UidRefKind;
|
|
26
|
+
/** One live node matching a uid PREFIX, projected to the fields the ambiguity decision and its error need. */
|
|
27
|
+
export interface IUidCandidate {
|
|
28
|
+
uid: string;
|
|
29
|
+
kind: string;
|
|
30
|
+
name: string | null;
|
|
31
|
+
isSuperseded: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The minimal executor a prefix candidate read needs. Structural, so it is
|
|
35
|
+
* satisfied by `AdapterTransaction`, graph-store's `GraphTransaction`, and a
|
|
36
|
+
* bare `StoreAdapter` alike — nothing here depends on which one it is.
|
|
37
|
+
*/
|
|
38
|
+
export interface IUidPrefixQueryExecutor {
|
|
39
|
+
executeAll<T = Record<string, unknown>>(sql: string, args?: unknown[]): Promise<{
|
|
40
|
+
rows: T[];
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The executor a uid resolution needs: the prefix candidate read plus the
|
|
45
|
+
* exact single-row read. Structural, so `AdapterTransaction`,
|
|
46
|
+
* graph-store's `GraphTransaction`, and a bare `StoreAdapter` all satisfy it.
|
|
47
|
+
*/
|
|
48
|
+
export interface INodeReadExecutor extends IUidPrefixQueryExecutor {
|
|
49
|
+
executeGet<T = Record<string, unknown>>(sql: string, args?: unknown[]): Promise<T | null>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Every LIVE node whose uid starts with `prefix`.
|
|
53
|
+
*
|
|
54
|
+
* The range predicate `uid >= ? AND uid < ?` is an indexed prefix scan (the
|
|
55
|
+
* store keeps a unique index on `uid`) and, unlike a `LIKE` pattern, is
|
|
56
|
+
* case-exact against the lowercase uids the store mints — so the input is
|
|
57
|
+
* lower-cased once here and the caller never has to reason about collation.
|
|
58
|
+
* Soft-deleted rows are excluded in SQL (`t_invalid IS NULL`); a superseded
|
|
59
|
+
* row is returned (its `is_superseded` flag carried) so a caller that resolves
|
|
60
|
+
* to it can raise the same stale-reference error the exact path does.
|
|
61
|
+
*/
|
|
62
|
+
export declare function queryLiveUidPrefixCandidates(exec: IUidPrefixQueryExecutor, prefix: string): Promise<IUidCandidate[]>;
|
|
63
|
+
/**
|
|
64
|
+
* The ambiguity decision, shared by both paths. Returns the sole candidate, or
|
|
65
|
+
* `null` when there are none (the caller owns the not-found error, which
|
|
66
|
+
* differs by kind). Two or more candidates throw {@link AmbiguousReferenceError}
|
|
67
|
+
* — the resolver never auto-selects.
|
|
68
|
+
*/
|
|
69
|
+
export declare function selectUniqueUidCandidate(ref: string, candidates: readonly IUidCandidate[]): IUidCandidate | null;
|
|
70
|
+
/**
|
|
71
|
+
* The not-found error for a uid reference, keyed by the kind the caller asked
|
|
72
|
+
* for. `asPrefix` selects the prefix-specific wording ("no item matches")
|
|
73
|
+
* versus the exact-uid wording, so a caller can tell "this short reference
|
|
74
|
+
* matched nothing" from "this full uid is not here."
|
|
75
|
+
*/
|
|
76
|
+
export declare function missingUidError(expectedKind: string | undefined, ref: string, asPrefix: boolean): Error;
|
|
77
|
+
/** The too-short refusal — a uid attempt below {@link MIN_UID_PREFIX_LENGTH}. */
|
|
78
|
+
export declare function tooShortUidError(ref: string): InvalidArgumentError;
|