@ultimat3/manifest 17.0.0 → 19.0.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/CLAUDE.md +8 -0
- package/package.json +7 -7
- package/src/build.ts +3 -0
- package/src/diff-fixtures.ts +1 -0
- package/src/diff-operations.ts +9 -0
- package/src/schema.ts +11 -0
- package/src/sources.ts +3 -0
package/CLAUDE.md
CHANGED
|
@@ -132,6 +132,14 @@ by the CLI, not imported.
|
|
|
132
132
|
**breaking**, so it also demands a major release of every APP that regenerates. Charging every
|
|
133
133
|
app a major for a field their readers never had to look at is a fix line that is not true.
|
|
134
134
|
`build.test.ts`'s `shape compatibility` case is the assertion — if it fails, the bump is earned.
|
|
135
|
+
- **`QueryFact.subscribes` is the one fact a tier-1 generator cannot reach any other way** (`As of
|
|
136
|
+
2026-08-26`). `@ultimat3/db` needs the tables a live query subscribes to so `x db gen` can grant
|
|
137
|
+
them `REPLICA IDENTITY FULL`, and `@ultimat3/cli` (tier 5) cannot import `@ultimat3/query`'s
|
|
138
|
+
runtime to ask — the manifest is the crossing. Written only when the read declared some:
|
|
139
|
+
`QueryDescriptor.subscribes` is `null` for a read that named none and an empty list is refused at
|
|
140
|
+
`query()`, so absence carries the whole meaning and no plain read pays a key for it. Classified
|
|
141
|
+
in `diff-operations.ts` as **internal** — no caller's contract moved — but reported, because the
|
|
142
|
+
list is what a migration is keyed on.
|
|
135
143
|
- `diff.ts` reads `mcp.expose` through `isMcpExposed` from `@ultimat3/core`, on **both** sides.
|
|
136
144
|
`before` is a file parsed off disk, so an older or hand-trimmed manifest can carry an absent or
|
|
137
145
|
non-boolean value that `!==` would classify from; and the fact `sources.ts` publishes has to be
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/manifest",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "19.0.0",
|
|
4
4
|
"description": "x.manifest.json: deterministic generated facts, contract diff, AGENTS.md budget",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
"LICENSE"
|
|
25
25
|
],
|
|
26
26
|
"engines": {
|
|
27
|
-
"bun": ">=1.
|
|
27
|
+
"bun": ">=1.4.0"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
31
31
|
"test": "bun test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ultimat3/action": "
|
|
35
|
-
"@ultimat3/core": "
|
|
36
|
-
"@ultimat3/entity": "
|
|
37
|
-
"@ultimat3/jobs": "
|
|
38
|
-
"@ultimat3/query": "
|
|
34
|
+
"@ultimat3/action": "19.0.0",
|
|
35
|
+
"@ultimat3/core": "19.0.0",
|
|
36
|
+
"@ultimat3/entity": "19.0.0",
|
|
37
|
+
"@ultimat3/jobs": "19.0.0",
|
|
38
|
+
"@ultimat3/query": "19.0.0"
|
|
39
39
|
}
|
|
40
40
|
}
|
package/src/build.ts
CHANGED
|
@@ -147,6 +147,9 @@ const normalizeQuery = (query: QueryFact): QueryFact => ({
|
|
|
147
147
|
...query,
|
|
148
148
|
permissions: [...query.permissions].sort(),
|
|
149
149
|
cacheTags: [...query.cacheTags].sort(),
|
|
150
|
+
// Conditional, not `?? []`: the fact is absent when the read declared nothing, and writing an
|
|
151
|
+
// empty array would make every plain read carry a key that means the same as no key.
|
|
152
|
+
...(query.subscribes === undefined ? {} : { subscribes: [...query.subscribes].sort() }),
|
|
150
153
|
});
|
|
151
154
|
|
|
152
155
|
// Job steps keep their DECLARED order — a job's steps are a sequence, not a set, and
|
package/src/diff-fixtures.ts
CHANGED
package/src/diff-operations.ts
CHANGED
|
@@ -106,6 +106,15 @@ export function diffQueries(
|
|
|
106
106
|
if (canonicalJson(query.cacheTags) !== canonicalJson(next.cacheTags)) {
|
|
107
107
|
changes.push({ kind: 'internal', path: `${path}.cacheTags`, detail: 'cache tags changed' });
|
|
108
108
|
}
|
|
109
|
+
// Internal for the same reason, and reported for a sharper one: this list is what `x db gen`
|
|
110
|
+
// grants REPLICA IDENTITY FULL to, so a move here is a migration the release owes.
|
|
111
|
+
if (canonicalJson(query.subscribes ?? null) !== canonicalJson(next.subscribes ?? null)) {
|
|
112
|
+
changes.push({
|
|
113
|
+
kind: 'internal',
|
|
114
|
+
path: `${path}.subscribes`,
|
|
115
|
+
detail: 'subscribed relations changed',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
109
118
|
}
|
|
110
119
|
for (const query of after) {
|
|
111
120
|
if (!beforeByName.has(query.name)) {
|
package/src/schema.ts
CHANGED
|
@@ -96,6 +96,17 @@ export interface QueryFact {
|
|
|
96
96
|
/** Every permission the policy asserts, flattened through the combinators, deduped and sorted. */
|
|
97
97
|
readonly permissions: readonly string[];
|
|
98
98
|
readonly live: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* The relations a live read is patched from, as the query DECLARED them — absent when it
|
|
101
|
+
* declared none, exactly like `ActionFact.rateLimit`, because an empty array on every plain
|
|
102
|
+
* read is bytes in a hand-reviewed file for no fact gained.
|
|
103
|
+
*
|
|
104
|
+
* The reason this fact exists: `x db gen` has to grant `REPLICA IDENTITY FULL` to those tables
|
|
105
|
+
* or `@ultimat3/realtime` refuses the subscription, and `@ultimat3/cli` cannot derive them —
|
|
106
|
+
* the relation name lives inside the query's `sql:` callback, which no generator can invoke
|
|
107
|
+
* without valid input. So the manifest is the one place a tier-1 generator can read it.
|
|
108
|
+
*/
|
|
109
|
+
readonly subscribes?: readonly string[];
|
|
99
110
|
readonly cacheTags: readonly string[];
|
|
100
111
|
}
|
|
101
112
|
|
package/src/sources.ts
CHANGED
|
@@ -81,6 +81,9 @@ export function frameworkSources(input: FrameworkSourcesInput): ManifestSources
|
|
|
81
81
|
policy: query.capability,
|
|
82
82
|
permissions: query.permissions,
|
|
83
83
|
live: query.live,
|
|
84
|
+
// Written only when declared — `null` is the descriptor's "the read named none", and an
|
|
85
|
+
// empty list is refused at `query()`, so absence carries the whole meaning.
|
|
86
|
+
...(query.subscribes === null ? {} : { subscribes: query.subscribes }),
|
|
84
87
|
cacheTags: query.tags,
|
|
85
88
|
})),
|
|
86
89
|
jobs: describeJobs().map((job) => ({
|