@wtfalch/foundry 0.58.0 → 0.60.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.
|
@@ -71,7 +71,8 @@ export interface WorkerAppDatabasePort {
|
|
|
71
71
|
applyMigration(owner: HyperdriveOrigin, migration: DatabaseMigration): Promise<void>;
|
|
72
72
|
/**
|
|
73
73
|
* Creates or updates the `runtime.user` role with `runtime.password`, grants it DML on the
|
|
74
|
-
* tables
|
|
74
|
+
* tables, revokes UPDATE, DELETE and TRUNCATE from it on each `appendOnly` table, and grants
|
|
75
|
+
* it EXECUTE on each `grants` function.
|
|
75
76
|
*/
|
|
76
77
|
ensureRuntimeRole(input: {
|
|
77
78
|
owner: HyperdriveOrigin;
|
|
@@ -80,6 +81,7 @@ export interface WorkerAppDatabasePort {
|
|
|
80
81
|
password: string;
|
|
81
82
|
};
|
|
82
83
|
appendOnly: string[];
|
|
84
|
+
grants: string[];
|
|
83
85
|
}): Promise<void>;
|
|
84
86
|
}
|
|
85
87
|
export declare const WORKER_APP_MIGRATION_TIMEOUT_MS = 300000;
|
|
@@ -149,6 +151,7 @@ export declare function applyWorkerAppDatabase(deps: WorkerAppDatabaseDeps, org:
|
|
|
149
151
|
artifactUrl: string;
|
|
150
152
|
database: {
|
|
151
153
|
appendOnly: string[];
|
|
154
|
+
grants?: string[];
|
|
152
155
|
migrationsSha256?: string;
|
|
153
156
|
};
|
|
154
157
|
}, context: StepExecutionContext, zoneApex: string): Promise<{
|
|
@@ -264,6 +264,7 @@ export async function applyWorkerAppDatabase(deps, org, entry, scriptName, want,
|
|
|
264
264
|
owner: ownerOrigin,
|
|
265
265
|
runtime: { user: runtimeUser, password: runtimePassword },
|
|
266
266
|
appendOnly: [...want.database.appendOnly],
|
|
267
|
+
grants: [...(want.database.grants ?? [])],
|
|
267
268
|
}));
|
|
268
269
|
// 6. Hyperdrive, with the runtime credentials only.
|
|
269
270
|
const name = hyperdriveName(scriptName);
|
|
@@ -107,6 +107,11 @@ export interface WorkerAppUpload {
|
|
|
107
107
|
migrations?: WorkerAppMigrations;
|
|
108
108
|
/** Durable Object classes the upload marks container-enabled; absent when the entry declares no container. */
|
|
109
109
|
containers?: string[];
|
|
110
|
+
/** Workers Logs: always on, every request sampled. The adapter sends it as the upload metadata's `observability`. */
|
|
111
|
+
observability: {
|
|
112
|
+
enabled: true;
|
|
113
|
+
head_sampling_rate: 1;
|
|
114
|
+
};
|
|
110
115
|
}
|
|
111
116
|
export interface WorkerAppContainer {
|
|
112
117
|
className: string;
|
|
@@ -203,6 +208,7 @@ export interface WorkerAppWant {
|
|
|
203
208
|
/** `workerApp.database`: the Hyperdrive binding and the tables the runtime role may only append to, sorted. */
|
|
204
209
|
database?: {
|
|
205
210
|
appendOnly: string[];
|
|
211
|
+
grants?: string[];
|
|
206
212
|
migrationsSha256?: string;
|
|
207
213
|
};
|
|
208
214
|
/** Secret names and where each comes from, sorted. */
|
package/dist/plan/worker-app.js
CHANGED
|
@@ -186,6 +186,7 @@ export function wantedWorkerApp(org, entry, recorded, input) {
|
|
|
186
186
|
? {
|
|
187
187
|
database: {
|
|
188
188
|
appendOnly: [...(app.database.appendOnly ?? [])].sort(),
|
|
189
|
+
...(app.database.grants?.length ? { grants: [...app.database.grants].sort() } : {}),
|
|
189
190
|
...(app.database.migrationsSha256
|
|
190
191
|
? { migrationsSha256: app.database.migrationsSha256 }
|
|
191
192
|
: {}),
|
|
@@ -526,6 +527,7 @@ export async function applyWorkerApp(deps, org, action, context) {
|
|
|
526
527
|
? [{ type: 'hyperdrive', name: WORKER_APP_HYPERDRIVE_BINDING, id: hyperdriveId }]
|
|
527
528
|
: []),
|
|
528
529
|
],
|
|
530
|
+
observability: { enabled: true, head_sampling_rate: 1 },
|
|
529
531
|
...(migrations ? { migrations } : {}),
|
|
530
532
|
...(want.containers.length > 0
|
|
531
533
|
? { containers: want.containers.map((c) => c.className) }
|
|
@@ -60,10 +60,14 @@ export interface WorkerAppConfig {
|
|
|
60
60
|
* or truncate. `migrationsSha256` (64 lowercase hex) pins the release's
|
|
61
61
|
* `migrations.json`: required when the release has one, and foundry refuses
|
|
62
62
|
* to parse bytes whose sha256 differs. Omit it only for a release with no
|
|
63
|
-
* `migrations.json`; one that has the file is then refused.
|
|
63
|
+
* `migrations.json`; one that has the file is then refused. `grants` names
|
|
64
|
+
* functions the runtime role may execute, as `name(type, ...)` or
|
|
65
|
+
* `schema.name(type, ...)`: the security definer functions a migration
|
|
66
|
+
* revoked from public, such as `@wtfalch/audit`'s ledger functions.
|
|
64
67
|
*/
|
|
65
68
|
database?: {
|
|
66
69
|
appendOnly?: string[];
|
|
70
|
+
grants?: string[];
|
|
67
71
|
migrationsSha256?: string;
|
|
68
72
|
};
|
|
69
73
|
/** Plain vars. Never secrets. */
|
|
@@ -95,6 +99,8 @@ export declare const entryHasDatabase: (entry: {
|
|
|
95
99
|
export declare const WORKER_APP_HYPERDRIVE_BINDING = "HYPERDRIVE";
|
|
96
100
|
/** The most tables `database.appendOnly` may name, and the shape of one name (`table` or `schema.table`). */
|
|
97
101
|
export declare const WORKER_APP_APPEND_ONLY_MAX = 100;
|
|
102
|
+
/** The most functions `database.grants` may name, and the shape of one signature. It is spliced into SQL, so nothing else passes. */
|
|
103
|
+
export declare const WORKER_APP_GRANTS_MAX = 100;
|
|
98
104
|
/** Hex characters of the org id's sha256 in a host id. */
|
|
99
105
|
export declare const WORKER_APP_HOST_ID_HASH_LENGTH = 12;
|
|
100
106
|
/**
|
|
@@ -27,6 +27,9 @@ export const WORKER_APP_HYPERDRIVE_BINDING = 'HYPERDRIVE';
|
|
|
27
27
|
/** The most tables `database.appendOnly` may name, and the shape of one name (`table` or `schema.table`). */
|
|
28
28
|
export const WORKER_APP_APPEND_ONLY_MAX = 100;
|
|
29
29
|
const TABLE_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,62}(\.[A-Za-z_][A-Za-z0-9_]{0,62})?$/;
|
|
30
|
+
/** The most functions `database.grants` may name, and the shape of one signature. It is spliced into SQL, so nothing else passes. */
|
|
31
|
+
export const WORKER_APP_GRANTS_MAX = 100;
|
|
32
|
+
const FUNCTION_SIGNATURE = /^[A-Za-z_][A-Za-z0-9_]{0,62}(\.[A-Za-z_][A-Za-z0-9_]{0,62})?\(([a-z][a-z0-9_]*( [a-z][a-z0-9_]*)?(, [a-z][a-z0-9_]*( [a-z][a-z0-9_]*)?)*)?\)$/;
|
|
30
33
|
/** The custody Worker's host id shape for a derived (app) host, and its limit. */
|
|
31
34
|
const HOST_ID = /^app-[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
32
35
|
const HOST_ID_MAX_LENGTH = 64;
|
|
@@ -342,11 +345,26 @@ function validateWorkerAppDatabase(at, app) {
|
|
|
342
345
|
if (database === undefined)
|
|
343
346
|
return null;
|
|
344
347
|
if (typeof database !== 'object' || database === null || Array.isArray(database)) {
|
|
345
|
-
return `${at} database must be an object: { appendOnly?: string[]; migrationsSha256?: string }`;
|
|
348
|
+
return `${at} database must be an object: { appendOnly?: string[]; grants?: string[]; migrationsSha256?: string }`;
|
|
346
349
|
}
|
|
347
|
-
const unknown = Object.keys(database).find((key) => key !== 'appendOnly' && key !== 'migrationsSha256');
|
|
350
|
+
const unknown = Object.keys(database).find((key) => key !== 'appendOnly' && key !== 'grants' && key !== 'migrationsSha256');
|
|
348
351
|
if (unknown) {
|
|
349
|
-
return `${at} database.${unknown} is not a setting: only appendOnly and migrationsSha256`;
|
|
352
|
+
return `${at} database.${unknown} is not a setting: only appendOnly, grants and migrationsSha256`;
|
|
353
|
+
}
|
|
354
|
+
const grants = database.grants;
|
|
355
|
+
if (grants !== undefined) {
|
|
356
|
+
if (!Array.isArray(grants) || grants.length > WORKER_APP_GRANTS_MAX) {
|
|
357
|
+
return `${at} database.grants must be a list of at most ${WORKER_APP_GRANTS_MAX} function signatures`;
|
|
358
|
+
}
|
|
359
|
+
const seenGrants = new Set();
|
|
360
|
+
for (const signature of grants) {
|
|
361
|
+
if (typeof signature !== 'string' || !FUNCTION_SIGNATURE.test(signature)) {
|
|
362
|
+
return `${at} database.grants names '${String(signature).slice(0, 80)}', which is not a function signature (name(type, ...) or schema.name(type, ...))`;
|
|
363
|
+
}
|
|
364
|
+
if (seenGrants.has(signature))
|
|
365
|
+
return `${at} database.grants names '${signature}' twice`;
|
|
366
|
+
seenGrants.add(signature);
|
|
367
|
+
}
|
|
350
368
|
}
|
|
351
369
|
const sha = database.migrationsSha256;
|
|
352
370
|
if (sha !== undefined && (typeof sha !== 'string' || !SHA256_HEX.test(sha))) {
|