@fuzdev/fuz_app 0.117.0 → 0.118.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/dist/auth/CLAUDE.md +17 -4
- package/dist/auth/audit_emitter.d.ts +13 -8
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +5 -4
- package/dist/auth/bootstrap_account.d.ts +3 -2
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +3 -3
- package/dist/auth/cleanup.d.ts +25 -11
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +47 -28
- package/dist/auth/deps.d.ts +1 -1
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +27 -0
- package/dist/auth/role_grant_offer_ddl.d.ts +4 -2
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_ddl.js +10 -3
- package/dist/auth/role_grant_offer_queries.d.ts +16 -7
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +33 -15
- package/dist/auth/role_grant_offer_schema.d.ts +9 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
- package/dist/db/migrate.d.ts +8 -13
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +9 -38
- package/dist/db/status.d.ts +0 -6
- package/dist/db/status.d.ts.map +1 -1
- package/dist/db/status.js +3 -23
- package/dist/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +9 -6
- package/dist/runtime/deps.d.ts +3 -2
- package/dist/runtime/deps.d.ts.map +1 -1
- package/dist/runtime/secure_file.d.ts +23 -9
- package/dist/runtime/secure_file.d.ts.map +1 -1
- package/dist/runtime/secure_file.js +29 -10
- package/dist/server/app_backend.d.ts +1 -1
- package/dist/testing/audit_drift_guard.d.ts +3 -5
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +3 -5
- package/dist/testing/cross_backend/expected_schema.json +1 -0
- package/dist/ui/AccountSessions.svelte +1 -1
- package/dist/ui/AdminAccounts.svelte +1 -1
- package/dist/ui/AdminAuditLog.svelte +1 -1
- package/dist/ui/AdminInvites.svelte +5 -4
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +6 -6
- package/dist/ui/AdminRoleGrantHistory.svelte +2 -2
- package/dist/ui/AdminSessions.svelte +1 -1
- package/dist/ui/AdminSettings.svelte +3 -3
- package/dist/ui/AdminSurface.svelte +1 -1
- package/dist/ui/RoleGrantOfferHistory.svelte +1 -1
- package/dist/ui/RoleGrantOfferInbox.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +7 -7
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAI9C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAI9C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;IACrC;;;;;;;OAOG;IACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD4D,CAAC;AAC5F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
|
package/dist/db/migrate.d.ts
CHANGED
|
@@ -86,7 +86,7 @@ export interface MigrationResult {
|
|
|
86
86
|
* Callers branch on `.kind` rather than matching error messages — message
|
|
87
87
|
* text is for operators, not control flow.
|
|
88
88
|
*/
|
|
89
|
-
export type MigrationErrorKind = 'binary-older-than-db' | 'name-divergence-at-N' | '
|
|
89
|
+
export type MigrationErrorKind = 'binary-older-than-db' | 'name-divergence-at-N' | 'migration-failed' | 'baseline-name-not-in-code' | 'baseline-name-out-of-order' | 'baseline-namespace-already-populated';
|
|
90
90
|
/** Structured context passed alongside a `MigrationError`. */
|
|
91
91
|
export interface MigrationErrorContext {
|
|
92
92
|
namespace?: string;
|
|
@@ -137,26 +137,22 @@ export declare class MigrationError extends Error {
|
|
|
137
137
|
* namespaces are omitted)
|
|
138
138
|
* @mutates schema_version - inserts one row per applied migration
|
|
139
139
|
* @throws MigrationError with `kind` of `binary-older-than-db`,
|
|
140
|
-
* `name-divergence-at-N`,
|
|
140
|
+
* `name-divergence-at-N`, or `migration-failed`
|
|
141
141
|
*/
|
|
142
142
|
export declare const run_migrations: (db: Db, namespaces: Array<MigrationNamespace>) => Promise<Array<MigrationResult>>;
|
|
143
143
|
/**
|
|
144
144
|
* Insert tracker rows for the named migrations of a namespace **without
|
|
145
145
|
* executing them**.
|
|
146
146
|
*
|
|
147
|
-
* Used to promote an existing schema (e.g. produced by
|
|
148
|
-
*
|
|
147
|
+
* Used to promote an existing schema (e.g. produced by an out-of-band
|
|
148
|
+
* bootstrap, or a hand-applied DDL set) into the identity tracker.
|
|
149
149
|
* `baseline()` trusts the operator-supplied list — it does not verify that
|
|
150
150
|
* the schema actually matches what the named migrations would have produced.
|
|
151
151
|
* Pair with a schema-assertion script post-baseline before re-enabling traffic.
|
|
152
152
|
*
|
|
153
153
|
* Contract:
|
|
154
|
-
* -
|
|
155
|
-
*
|
|
156
|
-
* table and the INSERT would fail with a confusing column-not-found).
|
|
157
|
-
* - Creates the new-shape `schema_version` table if missing — cutover
|
|
158
|
-
* scripts that just dropped the old-shape table can call `baseline()`
|
|
159
|
-
* directly with no separate DDL step.
|
|
154
|
+
* - Creates the `schema_version` table if missing — a promotion script can
|
|
155
|
+
* call `baseline()` against a fresh tracker with no separate DDL step.
|
|
160
156
|
* - Acquires the same per-namespace advisory lock as `run_migrations` (with
|
|
161
157
|
* the same try/catch fallback for environments lacking `pg_advisory_lock`).
|
|
162
158
|
* - Refuses if any tracker rows already exist *for this namespace* — lets
|
|
@@ -172,9 +168,8 @@ export declare const run_migrations: (db: Db, namespaces: Array<MigrationNamespa
|
|
|
172
168
|
* @param names - prefix of `ns.migrations[].name` to record as already-applied
|
|
173
169
|
* @mutates schema_version - inserts tracker rows for `names` without running
|
|
174
170
|
* the corresponding migration bodies
|
|
175
|
-
* @throws MigrationError with `kind` of `
|
|
176
|
-
* `baseline-name-
|
|
177
|
-
* `baseline-namespace-already-populated`
|
|
171
|
+
* @throws MigrationError with `kind` of `baseline-name-not-in-code`,
|
|
172
|
+
* `baseline-name-out-of-order`, or `baseline-namespace-already-populated`
|
|
178
173
|
*/
|
|
179
174
|
export declare const baseline: (db: Db, ns: MigrationNamespace, names: ReadonlyArray<string>) => Promise<void>;
|
|
180
175
|
//# sourceMappingURL=migrate.d.ts.map
|
package/dist/db/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC3B,sBAAsB,GACtB,sBAAsB,GACtB,
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC3B,sBAAsB,GACtB,sBAAsB,GACtB,kBAAkB,GAClB,2BAA2B,GAC3B,4BAA4B,GAC5B,sCAAsC,CAAC;AAE1C,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;gBAEnC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB;CAQtF;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,cAAc,GAC1B,IAAI,EAAE,EACN,YAAY,KAAK,CAAC,kBAAkB,CAAC,KACnC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAmFhC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,QAAQ,GACpB,IAAI,EAAE,EACN,IAAI,kBAAkB,EACtB,OAAO,aAAa,CAAC,MAAM,CAAC,KAC1B,OAAO,CAAC,IAAI,CA2Dd,CAAC"}
|
package/dist/db/migrate.js
CHANGED
|
@@ -83,24 +83,6 @@ CREATE TABLE IF NOT EXISTS schema_version (
|
|
|
83
83
|
PRIMARY KEY (namespace, name),
|
|
84
84
|
UNIQUE (namespace, sequence)
|
|
85
85
|
)`;
|
|
86
|
-
/**
|
|
87
|
-
* Detect the pre-0.42 `schema_version` shape (`namespace`, `version`,
|
|
88
|
-
* `applied_at`). The new-shape DDL uses `IF NOT EXISTS` and would silently
|
|
89
|
-
* no-op against the old table, so this probe runs before DDL and before any
|
|
90
|
-
* per-namespace lock.
|
|
91
|
-
*/
|
|
92
|
-
const detect_old_tracker = async (db) => {
|
|
93
|
-
const row = await db.query_one(`SELECT EXISTS (
|
|
94
|
-
SELECT 1 FROM information_schema.columns
|
|
95
|
-
WHERE table_schema = 'public'
|
|
96
|
-
AND table_name = 'schema_version'
|
|
97
|
-
AND column_name = 'version'
|
|
98
|
-
) as exists`);
|
|
99
|
-
return row?.exists ?? false;
|
|
100
|
-
};
|
|
101
|
-
const OLD_TRACKER_HINT = 'Detected fuz_app < 0.42 tracker shape (schema_version.version column exists). ' +
|
|
102
|
-
'Hint: `DROP TABLE schema_version` and re-run, or call `baseline()` first if ' +
|
|
103
|
-
'preserving an existing schema.';
|
|
104
86
|
/**
|
|
105
87
|
* Compute a stable int32 advisory lock key from a namespace string.
|
|
106
88
|
*
|
|
@@ -171,12 +153,9 @@ const with_namespace_lock = async (db, namespace, fn) => {
|
|
|
171
153
|
* namespaces are omitted)
|
|
172
154
|
* @mutates schema_version - inserts one row per applied migration
|
|
173
155
|
* @throws MigrationError with `kind` of `binary-older-than-db`,
|
|
174
|
-
* `name-divergence-at-N`,
|
|
156
|
+
* `name-divergence-at-N`, or `migration-failed`
|
|
175
157
|
*/
|
|
176
158
|
export const run_migrations = async (db, namespaces) => {
|
|
177
|
-
if (await detect_old_tracker(db)) {
|
|
178
|
-
throw new MigrationError('old-tracker-shape', OLD_TRACKER_HINT);
|
|
179
|
-
}
|
|
180
159
|
await db.query(SCHEMA_VERSION_DDL);
|
|
181
160
|
const results = [];
|
|
182
161
|
for (const { namespace, migrations } of namespaces) {
|
|
@@ -206,10 +185,10 @@ export const run_migrations = async (db, namespaces) => {
|
|
|
206
185
|
`that follow it, or delete from row ${i} onward to re-apply the tail).`, { namespace, at_index: i });
|
|
207
186
|
}
|
|
208
187
|
}
|
|
209
|
-
//
|
|
188
|
+
// up-to-date case
|
|
210
189
|
if (applied.length === migrations.length)
|
|
211
190
|
return;
|
|
212
|
-
// Step
|
|
191
|
+
// Step 5: run pending tail in a single chain-tx
|
|
213
192
|
let next_sequence = applied.length > 0 ? applied[applied.length - 1].sequence + 1 : 0;
|
|
214
193
|
const applied_names = [];
|
|
215
194
|
await db.transaction(async (tx) => {
|
|
@@ -240,19 +219,15 @@ export const run_migrations = async (db, namespaces) => {
|
|
|
240
219
|
* Insert tracker rows for the named migrations of a namespace **without
|
|
241
220
|
* executing them**.
|
|
242
221
|
*
|
|
243
|
-
* Used to promote an existing schema (e.g. produced by
|
|
244
|
-
*
|
|
222
|
+
* Used to promote an existing schema (e.g. produced by an out-of-band
|
|
223
|
+
* bootstrap, or a hand-applied DDL set) into the identity tracker.
|
|
245
224
|
* `baseline()` trusts the operator-supplied list — it does not verify that
|
|
246
225
|
* the schema actually matches what the named migrations would have produced.
|
|
247
226
|
* Pair with a schema-assertion script post-baseline before re-enabling traffic.
|
|
248
227
|
*
|
|
249
228
|
* Contract:
|
|
250
|
-
* -
|
|
251
|
-
*
|
|
252
|
-
* table and the INSERT would fail with a confusing column-not-found).
|
|
253
|
-
* - Creates the new-shape `schema_version` table if missing — cutover
|
|
254
|
-
* scripts that just dropped the old-shape table can call `baseline()`
|
|
255
|
-
* directly with no separate DDL step.
|
|
229
|
+
* - Creates the `schema_version` table if missing — a promotion script can
|
|
230
|
+
* call `baseline()` against a fresh tracker with no separate DDL step.
|
|
256
231
|
* - Acquires the same per-namespace advisory lock as `run_migrations` (with
|
|
257
232
|
* the same try/catch fallback for environments lacking `pg_advisory_lock`).
|
|
258
233
|
* - Refuses if any tracker rows already exist *for this namespace* — lets
|
|
@@ -268,14 +243,10 @@ export const run_migrations = async (db, namespaces) => {
|
|
|
268
243
|
* @param names - prefix of `ns.migrations[].name` to record as already-applied
|
|
269
244
|
* @mutates schema_version - inserts tracker rows for `names` without running
|
|
270
245
|
* the corresponding migration bodies
|
|
271
|
-
* @throws MigrationError with `kind` of `
|
|
272
|
-
* `baseline-name-
|
|
273
|
-
* `baseline-namespace-already-populated`
|
|
246
|
+
* @throws MigrationError with `kind` of `baseline-name-not-in-code`,
|
|
247
|
+
* `baseline-name-out-of-order`, or `baseline-namespace-already-populated`
|
|
274
248
|
*/
|
|
275
249
|
export const baseline = async (db, ns, names) => {
|
|
276
|
-
if (await detect_old_tracker(db)) {
|
|
277
|
-
throw new MigrationError('old-tracker-shape', OLD_TRACKER_HINT, { namespace: ns.namespace });
|
|
278
|
-
}
|
|
279
250
|
await db.query(SCHEMA_VERSION_DDL);
|
|
280
251
|
const code_names = ns.migrations.map((m) => m.name);
|
|
281
252
|
// Validate every supplied name exists in code (catches drift between cutover
|
package/dist/db/status.d.ts
CHANGED
|
@@ -82,12 +82,6 @@ export interface DbStatus {
|
|
|
82
82
|
tables: Array<TableStatus>;
|
|
83
83
|
/** Per-namespace migration status. */
|
|
84
84
|
migrations: Array<MigrationStatus>;
|
|
85
|
-
/**
|
|
86
|
-
* True if the pre-0.42 `schema_version` shape (with a `version` column)
|
|
87
|
-
* was detected. The runner refuses to start in this state — operators
|
|
88
|
-
* see this flag as their cue to drop the table or call `baseline()`.
|
|
89
|
-
*/
|
|
90
|
-
old_tracker_shape?: boolean;
|
|
91
85
|
}
|
|
92
86
|
/**
|
|
93
87
|
* Query database status including connectivity, tables, and migration state.
|
package/dist/db/status.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,mEAAmE;IACnE,IAAI,EAAE,eAAe,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAChB,GACD;IACA,kEAAkE;IAClE,IAAI,EAAE,cAAc,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAChB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,sCAAsC;IACtC,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GACnB;IACA,mEAAmE;IACnE,IAAI,EAAE,eAAe,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;CAChB,GACD;IACA,kEAAkE;IAClE,IAAI,EAAE,cAAc,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAChB,CAAC;AAEL;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,2EAA2E;IAC3E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC3B,sCAAsC;IACtC,UAAU,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CACnC;AAaD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,IAAI,EAAE,EACN,aAAa,KAAK,CAAC,kBAAkB,CAAC,KACpC,OAAO,CAAC,QAAQ,CAsGlB,CAAC;AAeF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,QAAQ,KAAG,MA4CnD,CAAC"}
|
package/dist/db/status.js
CHANGED
|
@@ -12,15 +12,6 @@
|
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
14
|
import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
15
|
-
const has_table_column = async (db, table_name, column_name) => {
|
|
16
|
-
const row = await db.query_one(`SELECT EXISTS (
|
|
17
|
-
SELECT 1 FROM information_schema.columns
|
|
18
|
-
WHERE table_schema = 'public'
|
|
19
|
-
AND table_name = $1
|
|
20
|
-
AND column_name = $2
|
|
21
|
-
) as exists`, [table_name, column_name]);
|
|
22
|
-
return row?.exists ?? false;
|
|
23
|
-
};
|
|
24
15
|
const has_table = async (db, table_name) => {
|
|
25
16
|
const row = await db.query_one(`SELECT EXISTS (
|
|
26
17
|
SELECT 1 FROM information_schema.tables
|
|
@@ -69,14 +60,9 @@ export const query_db_status = async (db, namespaces) => {
|
|
|
69
60
|
}
|
|
70
61
|
// check migration state
|
|
71
62
|
const migrations = [];
|
|
72
|
-
let old_tracker_shape;
|
|
73
63
|
if (namespaces?.length) {
|
|
74
64
|
const sv_exists = await has_table(db, 'schema_version');
|
|
75
|
-
|
|
76
|
-
const old_shape = sv_exists ? await has_table_column(db, 'schema_version', 'version') : false;
|
|
77
|
-
if (old_shape)
|
|
78
|
-
old_tracker_shape = true;
|
|
79
|
-
if (sv_exists && !old_shape) {
|
|
65
|
+
if (sv_exists) {
|
|
80
66
|
for (const { namespace, migrations: ns_migrations } of namespaces) {
|
|
81
67
|
const rows = await db.query(`SELECT name FROM schema_version
|
|
82
68
|
WHERE namespace = $1
|
|
@@ -118,7 +104,7 @@ export const query_db_status = async (db, namespaces) => {
|
|
|
118
104
|
}
|
|
119
105
|
}
|
|
120
106
|
else {
|
|
121
|
-
// no tracker
|
|
107
|
+
// no tracker — every namespace shows as "nothing applied yet"
|
|
122
108
|
for (const { namespace, migrations: ns_migrations } of namespaces) {
|
|
123
109
|
const code_names = ns_migrations.map((m) => m.name);
|
|
124
110
|
migrations.push({
|
|
@@ -134,8 +120,7 @@ export const query_db_status = async (db, namespaces) => {
|
|
|
134
120
|
connected: true,
|
|
135
121
|
table_count: tables.length,
|
|
136
122
|
tables,
|
|
137
|
-
migrations
|
|
138
|
-
...(old_tracker_shape ? { old_tracker_shape: true } : {})
|
|
123
|
+
migrations
|
|
139
124
|
};
|
|
140
125
|
};
|
|
141
126
|
/**
|
|
@@ -171,11 +156,6 @@ export const format_db_status = (status) => {
|
|
|
171
156
|
lines.push(` ${t.name.padEnd(max_name)} ${t.row_count} rows`);
|
|
172
157
|
}
|
|
173
158
|
}
|
|
174
|
-
if (status.old_tracker_shape) {
|
|
175
|
-
lines.push('');
|
|
176
|
-
lines.push(' Migrations: pre-0.42 schema_version shape detected.');
|
|
177
|
-
lines.push(' Drop the table and re-run, or call `baseline()` first if preserving the schema.');
|
|
178
|
-
}
|
|
179
159
|
if (status.migrations.length > 0) {
|
|
180
160
|
lines.push('');
|
|
181
161
|
lines.push(' Migrations:');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAA6B,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,WAAW,EAA6B,MAAM,WAAW,CAAC;AAyExE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,WA0KhE,CAAC"}
|
package/dist/runtime/deno.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
11
|
-
import { assert_secure_mode, assert_secure_size, read_secure_bounded } from './secure_file.js';
|
|
11
|
+
import { assert_secure_mode, assert_secure_regular_file, assert_secure_size, read_secure_bounded } from './secure_file.js';
|
|
12
12
|
/**
|
|
13
13
|
* Create a runtime backed by Deno APIs.
|
|
14
14
|
*
|
|
@@ -46,17 +46,20 @@ export const create_deno_runtime = (args) => ({
|
|
|
46
46
|
read_text_file: (path) => Deno.readTextFile(path),
|
|
47
47
|
read_file: (path) => Deno.readFile(path),
|
|
48
48
|
read_secure_file: async (path) => {
|
|
49
|
-
// Deno exposes no O_NOFOLLOW, so the symlink
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
49
|
+
// Deno exposes no O_NOFOLLOW or O_NONBLOCK, so the symlink and
|
|
50
|
+
// regular-file checks are a pre-open lstat (a TOCTOU window the Node
|
|
51
|
+
// impl doesn't have — a FIFO swapped in after it would block the open;
|
|
52
|
+
// the checks below still run on the open descriptor, so a post-open swap
|
|
53
|
+
// can't defeat them). The checks themselves are the shared
|
|
54
|
+
// `runtime/secure_file.ts` helpers, same as `load_secure_file_node`.
|
|
54
55
|
const l = await Deno.lstat(path);
|
|
55
56
|
if (l.isSymlink)
|
|
56
57
|
throw new Error(`secure file is a symlink: ${path}`);
|
|
58
|
+
assert_secure_regular_file(path, l.isFile);
|
|
57
59
|
const file = await Deno.open(path, { read: true });
|
|
58
60
|
try {
|
|
59
61
|
const s = await file.stat();
|
|
62
|
+
assert_secure_regular_file(path, s.isFile);
|
|
60
63
|
// `mode` is null where POSIX modes don't apply (Windows).
|
|
61
64
|
if (s.mode !== null)
|
|
62
65
|
assert_secure_mode(path, s.mode);
|
package/dist/runtime/deps.d.ts
CHANGED
|
@@ -103,8 +103,9 @@ export interface WriteFileOptions {
|
|
|
103
103
|
*/
|
|
104
104
|
export interface FsSecureReadDeps {
|
|
105
105
|
/**
|
|
106
|
-
* Read a small secret file with fail-loud checks: rejects symlinks,
|
|
107
|
-
*
|
|
106
|
+
* Read a small secret file with fail-loud checks: rejects symlinks,
|
|
107
|
+
* non-regular files (FIFO, device, directory — real runtimes only; the mock
|
|
108
|
+
* has no file types), any group/other-accessible mode (must be `0600`/`0400`; skipped where POSIX
|
|
108
109
|
* modes don't apply), and files over a 4 KiB cap. Permission checks run on
|
|
109
110
|
* the open descriptor where the platform allows, so the file can't be
|
|
110
111
|
* swapped between check and read. Throws on any refusal — a refused secret
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACvB,yCAAyC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC9C,mCAAmC;IACnC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACvB,yCAAyC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC9C,mCAAmC;IACnC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;;;;;;OAQG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACxD;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,+DAA+D;IAC/D,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,8DAA8D;IAC9D,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,+DAA+D;IAC/D,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACjD;;;;;;OAMG;IACH,qBAAqB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC3F,8FAA8F;IAC9F,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;CAClD;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,sFAAsF;IACtF,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,6EAA6E;IAC7E,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9F,6BAA6B;IAC7B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,4BAA4B;IAC5B,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D;;;;;;;;;;OAUG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC5B;;;;;OAKG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IACxE;;;;;OAKG;IACH,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrF;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,kCAAkC;IAClC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B;;;;;;;OAOG;IACH,WAAW,EAAE,CACZ,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EACnB,OAAO,CAAC,EAAE,iBAAiB,KACvB,OAAO,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,yDAAyD;IACzD,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACvB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,6BAA6B;IAC7B,YAAY,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,6CAA6C;IAC7C,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,oCAAoC;IACpC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAChB,SACC,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,SAAS,EACT,YAAY,EACZ,WAAW,EACX,OAAO;IACR,qCAAqC;IACrC,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,qCAAqC;IACrC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,qFAAqF;IACrF,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3E"}
|
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The file this guards is typically the highest-value credential on the host
|
|
6
6
|
* (the bootstrap token mints the keeper account), so the read fails loud
|
|
7
|
-
* rather than degrading: a symlink, a
|
|
8
|
-
*
|
|
9
|
-
* token at `0600`, so the check
|
|
10
|
-
* hand-placed `0644` file fails at boot
|
|
7
|
+
* rather than degrading: a symlink, a non-regular file (FIFO, device node,
|
|
8
|
+
* directory), a group/other-accessible mode, or an oversized file is refused,
|
|
9
|
+
* never returned. The deploy recipe places the token at `0600`, so the check
|
|
10
|
+
* ratifies the shape zap produces; a hand-placed `0644` file fails at boot
|
|
11
|
+
* instead of being read.
|
|
11
12
|
*
|
|
12
13
|
* The Node implementation lives here (`load_secure_file_node`, wired as
|
|
13
14
|
* `create_node_runtime().read_secure_file`); the Deno runtime implements the
|
|
14
15
|
* same contract over `Deno.open` in `runtime/deno.ts`, and the mock honors
|
|
15
|
-
* the mode/size checks over its in-memory map
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* the mode/size checks over its in-memory map (it has no file types). The real
|
|
17
|
+
* runtimes refuse through the shared `assert_secure_regular_file` /
|
|
18
|
+
* `assert_secure_mode` / `assert_secure_size` / `read_secure_bounded` helpers,
|
|
19
|
+
* and the mock through the mode and size ones, so the checks — and their
|
|
20
|
+
* operator-facing messages — can't drift between runtimes.
|
|
19
21
|
*
|
|
20
22
|
* @module
|
|
21
23
|
*/
|
|
@@ -25,6 +27,13 @@
|
|
|
25
27
|
* `MAX_SECURE_FILE_SIZE`.
|
|
26
28
|
*/
|
|
27
29
|
export declare const MAX_SECURE_FILE_SIZE = 4096;
|
|
30
|
+
/**
|
|
31
|
+
* Refuse anything but a regular file — a FIFO, device node, socket, or
|
|
32
|
+
* directory at the secret's path.
|
|
33
|
+
*
|
|
34
|
+
* @throws Error naming the path
|
|
35
|
+
*/
|
|
36
|
+
export declare const assert_secure_regular_file: (path: string, is_file: boolean) => void;
|
|
28
37
|
/**
|
|
29
38
|
* Refuse any group/other-accessible mode (only `0600`/`0400` pass).
|
|
30
39
|
*
|
|
@@ -57,6 +66,11 @@ export declare const read_secure_bounded: (path: string, read_chunk: (target: Ui
|
|
|
57
66
|
* Read a secret file with fail-loud checks (Node implementation).
|
|
58
67
|
*
|
|
59
68
|
* - `O_NOFOLLOW` atomically rejects symlinks during open (no TOCTOU window)
|
|
69
|
+
* - `O_NONBLOCK` keeps the open from waiting on a FIFO that has no writer (it
|
|
70
|
+
* has no effect on a regular file); the regular-file check on the open
|
|
71
|
+
* descriptor then refuses it, a device node, or a directory
|
|
72
|
+
* - `O_NOCTTY` keeps a terminal device at the path from becoming the process's
|
|
73
|
+
* controlling terminal in the moment before that check refuses it
|
|
60
74
|
* - the permission check runs on the open descriptor, not the path, so the
|
|
61
75
|
* file can't be swapped between check and read — any group/other access
|
|
62
76
|
* (not `0600`/`0400`) is refused
|
|
@@ -65,7 +79,7 @@ export declare const read_secure_bounded: (path: string, read_chunk: (target: Ui
|
|
|
65
79
|
*
|
|
66
80
|
* @param path - path to the secret file
|
|
67
81
|
* @returns the file's bytes
|
|
68
|
-
* @throws Error on a missing file, symlink, permissive mode, oversized file, or I/O failure
|
|
82
|
+
* @throws Error on a missing file, symlink, non-regular file, permissive mode, oversized file, or I/O failure
|
|
69
83
|
*/
|
|
70
84
|
export declare const load_secure_file_node: (path: string) => Promise<Uint8Array>;
|
|
71
85
|
//# sourceMappingURL=secure_file.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secure_file.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/secure_file.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"secure_file.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/secure_file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAUzC;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,EAAE,SAAS,OAAO,KAAG,IAI3E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,IAM/D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,IAI/D,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,MAAM,EACZ,YAAY,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,KACxD,OAAO,CAAC,UAAU,CAUpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,UAAU,CA2B5E,CAAC"}
|
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The file this guards is typically the highest-value credential on the host
|
|
6
6
|
* (the bootstrap token mints the keeper account), so the read fails loud
|
|
7
|
-
* rather than degrading: a symlink, a
|
|
8
|
-
*
|
|
9
|
-
* token at `0600`, so the check
|
|
10
|
-
* hand-placed `0644` file fails at boot
|
|
7
|
+
* rather than degrading: a symlink, a non-regular file (FIFO, device node,
|
|
8
|
+
* directory), a group/other-accessible mode, or an oversized file is refused,
|
|
9
|
+
* never returned. The deploy recipe places the token at `0600`, so the check
|
|
10
|
+
* ratifies the shape zap produces; a hand-placed `0644` file fails at boot
|
|
11
|
+
* instead of being read.
|
|
11
12
|
*
|
|
12
13
|
* The Node implementation lives here (`load_secure_file_node`, wired as
|
|
13
14
|
* `create_node_runtime().read_secure_file`); the Deno runtime implements the
|
|
14
15
|
* same contract over `Deno.open` in `runtime/deno.ts`, and the mock honors
|
|
15
|
-
* the mode/size checks over its in-memory map
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* the mode/size checks over its in-memory map (it has no file types). The real
|
|
17
|
+
* runtimes refuse through the shared `assert_secure_regular_file` /
|
|
18
|
+
* `assert_secure_mode` / `assert_secure_size` / `read_secure_bounded` helpers,
|
|
19
|
+
* and the mock through the mode and size ones, so the checks — and their
|
|
20
|
+
* operator-facing messages — can't drift between runtimes.
|
|
19
21
|
*
|
|
20
22
|
* @module
|
|
21
23
|
*/
|
|
@@ -35,6 +37,17 @@ export const MAX_SECURE_FILE_SIZE = 4096;
|
|
|
35
37
|
* The mock runtime checks unconditionally — its modes are simulated.
|
|
36
38
|
*/
|
|
37
39
|
const modes_apply = process.platform !== 'win32';
|
|
40
|
+
/**
|
|
41
|
+
* Refuse anything but a regular file — a FIFO, device node, socket, or
|
|
42
|
+
* directory at the secret's path.
|
|
43
|
+
*
|
|
44
|
+
* @throws Error naming the path
|
|
45
|
+
*/
|
|
46
|
+
export const assert_secure_regular_file = (path, is_file) => {
|
|
47
|
+
if (!is_file) {
|
|
48
|
+
throw new Error(`not a regular file: ${path}`);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
38
51
|
/**
|
|
39
52
|
* Refuse any group/other-accessible mode (only `0600`/`0400` pass).
|
|
40
53
|
*
|
|
@@ -86,6 +99,11 @@ export const read_secure_bounded = async (path, read_chunk) => {
|
|
|
86
99
|
* Read a secret file with fail-loud checks (Node implementation).
|
|
87
100
|
*
|
|
88
101
|
* - `O_NOFOLLOW` atomically rejects symlinks during open (no TOCTOU window)
|
|
102
|
+
* - `O_NONBLOCK` keeps the open from waiting on a FIFO that has no writer (it
|
|
103
|
+
* has no effect on a regular file); the regular-file check on the open
|
|
104
|
+
* descriptor then refuses it, a device node, or a directory
|
|
105
|
+
* - `O_NOCTTY` keeps a terminal device at the path from becoming the process's
|
|
106
|
+
* controlling terminal in the moment before that check refuses it
|
|
89
107
|
* - the permission check runs on the open descriptor, not the path, so the
|
|
90
108
|
* file can't be swapped between check and read — any group/other access
|
|
91
109
|
* (not `0600`/`0400`) is refused
|
|
@@ -94,12 +112,12 @@ export const read_secure_bounded = async (path, read_chunk) => {
|
|
|
94
112
|
*
|
|
95
113
|
* @param path - path to the secret file
|
|
96
114
|
* @returns the file's bytes
|
|
97
|
-
* @throws Error on a missing file, symlink, permissive mode, oversized file, or I/O failure
|
|
115
|
+
* @throws Error on a missing file, symlink, non-regular file, permissive mode, oversized file, or I/O failure
|
|
98
116
|
*/
|
|
99
117
|
export const load_secure_file_node = async (path) => {
|
|
100
118
|
let handle;
|
|
101
119
|
try {
|
|
102
|
-
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
120
|
+
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK | constants.O_NOCTTY);
|
|
103
121
|
}
|
|
104
122
|
catch (err) {
|
|
105
123
|
// ELOOP means the path was a symlink (O_NOFOLLOW refused it).
|
|
@@ -110,6 +128,7 @@ export const load_secure_file_node = async (path) => {
|
|
|
110
128
|
}
|
|
111
129
|
try {
|
|
112
130
|
const s = await handle.stat();
|
|
131
|
+
assert_secure_regular_file(path, s.isFile());
|
|
113
132
|
if (modes_apply)
|
|
114
133
|
assert_secure_mode(path, s.mode);
|
|
115
134
|
assert_secure_size(path, s.size);
|
|
@@ -85,7 +85,7 @@ export interface CreateAppBackendOptions {
|
|
|
85
85
|
/**
|
|
86
86
|
* Hardened secret-file read for the bootstrap token — pass the runtime's
|
|
87
87
|
* `read_secure_file` (see `FsSecureReadDeps`). Rejects symlinks,
|
|
88
|
-
* group/other-accessible modes, and oversized files.
|
|
88
|
+
* non-regular files, group/other-accessible modes, and oversized files.
|
|
89
89
|
*/
|
|
90
90
|
read_secure_file: (path: string) => Promise<Uint8Array>;
|
|
91
91
|
/** Delete a file. */
|
|
@@ -60,9 +60,8 @@ export interface RecordingAuditEmitter {
|
|
|
60
60
|
* `listener_count` reports (registered listeners never fire — this emitter
|
|
61
61
|
* captures `emit` shapes, not fan-out).
|
|
62
62
|
*
|
|
63
|
-
* `emit` AND `emit_pool` both append to `calls
|
|
64
|
-
*
|
|
65
|
-
* read assertions off the same array.
|
|
63
|
+
* `emit` AND `emit_pool` both append to `calls`, so a test reads either
|
|
64
|
+
* entry point's writes off the same array.
|
|
66
65
|
*
|
|
67
66
|
* Pass `calls_ref` to write into a caller-owned array (callers that
|
|
68
67
|
* declared `const events: Array<AuditLogInput> = []` and want to keep
|
|
@@ -99,8 +98,7 @@ export declare const create_recording_audit_emitter: (calls_ref?: Array<AuditLog
|
|
|
99
98
|
* role-grant-shape emissions land in `events_ref` alongside bare `emit`
|
|
100
99
|
* calls. `emit_pool` and `notify` are not decorated — they take
|
|
101
100
|
* `AuditLogInput` / `AuditLogEvent` directly without going through
|
|
102
|
-
* `emit`, so
|
|
103
|
-
* `auth/cleanup.ts`) skip capture. Close-firing handlers all reach for
|
|
101
|
+
* `emit`, so `emit_pool` writes skip capture. Close-firing handlers all reach for
|
|
104
102
|
* `emit` or `emit_role_grant_target`, so the ordering test sees them
|
|
105
103
|
* regardless of which entry point a future refactor picks.
|
|
106
104
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,yBAAyB,EAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED
|
|
1
|
+
{"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,yBAAyB,EAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBA8BF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,kCAAkC,GAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACxF,SAAS;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAC1B,YAAY,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,EACtC,gBAAgB,IAAI,CAAC,yBAAyB,EAAE,IAAI,GAAG,KAAK,GAAG,gBAAgB,CAAC,KAC9E,YAWF,CAAC"}
|
|
@@ -48,9 +48,8 @@ export const install_audit_drift_guard = () => {
|
|
|
48
48
|
* `listener_count` reports (registered listeners never fire — this emitter
|
|
49
49
|
* captures `emit` shapes, not fan-out).
|
|
50
50
|
*
|
|
51
|
-
* `emit` AND `emit_pool` both append to `calls
|
|
52
|
-
*
|
|
53
|
-
* read assertions off the same array.
|
|
51
|
+
* `emit` AND `emit_pool` both append to `calls`, so a test reads either
|
|
52
|
+
* entry point's writes off the same array.
|
|
54
53
|
*
|
|
55
54
|
* Pass `calls_ref` to write into a caller-owned array (callers that
|
|
56
55
|
* declared `const events: Array<AuditLogInput> = []` and want to keep
|
|
@@ -117,8 +116,7 @@ export const create_recording_audit_emitter = (calls_ref) => {
|
|
|
117
116
|
* role-grant-shape emissions land in `events_ref` alongside bare `emit`
|
|
118
117
|
* calls. `emit_pool` and `notify` are not decorated — they take
|
|
119
118
|
* `AuditLogInput` / `AuditLogEvent` directly without going through
|
|
120
|
-
* `emit`, so
|
|
121
|
-
* `auth/cleanup.ts`) skip capture. Close-firing handlers all reach for
|
|
119
|
+
* `emit`, so `emit_pool` writes skip capture. Close-firing handlers all reach for
|
|
122
120
|
* `emit` or `emit_role_grant_target`, so the ordering test sees them
|
|
123
121
|
* regardless of which entry point a future refactor picks.
|
|
124
122
|
*
|
|
@@ -53,10 +53,11 @@
|
|
|
53
53
|
</script>
|
|
54
54
|
|
|
55
55
|
<section>
|
|
56
|
-
<h1>
|
|
57
|
-
<
|
|
56
|
+
<h1>Invites</h1>
|
|
57
|
+
<fieldset>
|
|
58
|
+
<legend>Signup</legend>
|
|
58
59
|
<OpenSignupToggle />
|
|
59
|
-
</
|
|
60
|
+
</fieldset>
|
|
60
61
|
{#if admin_invites.invite_count > 0}
|
|
61
62
|
<p>
|
|
62
63
|
<span class="chip color_a">
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
}}
|
|
74
75
|
>
|
|
75
76
|
<fieldset class="row gap_sm">
|
|
76
|
-
<legend>
|
|
77
|
+
<legend>Invite Target</legend>
|
|
77
78
|
<label class="grow">
|
|
78
79
|
<div class="title">email</div>
|
|
79
80
|
<input
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminInvites.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminInvites.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminInvites.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminInvites.svelte"],"names":[],"mappings":"AAwJA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|