@fuzdev/fuz_app 0.25.0 → 0.27.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/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +12 -5
- package/dist/dev/setup.d.ts +34 -0
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +48 -0
- package/dist/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +66 -13
- package/dist/runtime/deps.d.ts +48 -4
- package/dist/runtime/deps.d.ts.map +1 -1
- package/dist/runtime/mock.d.ts +3 -2
- package/dist/runtime/mock.d.ts.map +1 -1
- package/dist/runtime/mock.js +60 -4
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +58 -6
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,MAAM,CAAC;AACxC,OAAO,KAAK,EAAC,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEzD,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAgB1E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AAErC,OAAO,EAAC,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAG7F,OAAO,EAAC,yBAAyB,EAAE,KAAK,kBAAkB,EAAC,MAAM,4BAA4B,CAAC;AAE9F,YAAY,EAAC,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAC,CAAC;AAE1D,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IACjC,qFAAqF;IACrF,EAAE,EAAE,SAAS,CAAC;IACd,4EAA4E;IAC5E,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,wFAAwF;IACxF,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAClC,+CAA+C;IAC/C,EAAE,EAAE,SAAS,CAAC;IACd,2CAA2C;IAC3C,aAAa,EAAE,IAAI,CAAC;IACpB,kGAAkG;IAClG,QAAQ,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wCAAwC;AACxC,MAAM,WAAW,uBAAuB,CAAC,IAAI,SAAS,kBAAkB;IACvE,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,iEAAiE;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;;;;;OAMG;IACH,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC;;;;;OAKG;IACH,cAAc,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/D;;;;OAIG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;IAC7C,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED,sCAAsC;AACtC,MAAM,WAAW,sBAAsB;IACtC,yEAAyE;IACzE,SAAS,EAAE,yBAAyB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,SAAS,kBAAkB,EACjE,SAAS,uBAAuB,CAAC,IAAI,CAAC,KACpC,
|
|
1
|
+
{"version":3,"file":"register_action_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_action_ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,MAAM,CAAC;AACxC,OAAO,KAAK,EAAC,gBAAgB,EAAE,SAAS,EAAC,MAAM,SAAS,CAAC;AAEzD,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAgB1E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AAErC,OAAO,EAAC,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAG7F,OAAO,EAAC,yBAAyB,EAAE,KAAK,kBAAkB,EAAC,MAAM,4BAA4B,CAAC;AAE9F,YAAY,EAAC,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAC,CAAC;AAE1D,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IACjC,qFAAqF;IACrF,EAAE,EAAE,SAAS,CAAC;IACd,4EAA4E;IAC5E,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,wFAAwF;IACxF,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IAClC,+CAA+C;IAC/C,EAAE,EAAE,SAAS,CAAC;IACd,2CAA2C;IAC3C,aAAa,EAAE,IAAI,CAAC;IACpB,kGAAkG;IAClG,QAAQ,EAAE,kBAAkB,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wCAAwC;AACxC,MAAM,WAAW,uBAAuB,CAAC,IAAI,SAAS,kBAAkB;IACvE,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,iEAAiE;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC;;;;;;OAMG;IACH,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC;;;;;OAKG;IACH,cAAc,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/D;;;;OAIG;IACH,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;IAC7C,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED,sCAAsC;AACtC,MAAM,WAAW,sBAAsB;IACtC,yEAAyE;IACzE,SAAS,EAAE,yBAAyB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,SAAS,kBAAkB,EACjE,SAAS,uBAAuB,CAAC,IAAI,CAAC,KACpC,sBA8WF,CAAC"}
|
|
@@ -28,7 +28,7 @@ import { get_request_context, has_role } from '../auth/request_context.js';
|
|
|
28
28
|
import { hash_session_token } from '../auth/session_queries.js';
|
|
29
29
|
import { ROLE_KEEPER } from '../auth/role_schema.js';
|
|
30
30
|
import { JSONRPC_VERSION } from '../http/jsonrpc.js';
|
|
31
|
-
import { jsonrpc_error_messages } from '../http/jsonrpc_errors.js';
|
|
31
|
+
import { jsonrpc_error_messages, ThrownJsonrpcError } from '../http/jsonrpc_errors.js';
|
|
32
32
|
import { create_jsonrpc_error_response, create_jsonrpc_error_response_from_thrown, create_jsonrpc_notification, to_jsonrpc_message_id, to_jsonrpc_params, is_jsonrpc_request, } from '../http/jsonrpc_helpers.js';
|
|
33
33
|
import { CREDENTIAL_TYPE_KEY, AUTH_API_TOKEN_ID_KEY } from '../hono_context.js';
|
|
34
34
|
import {} from './action_types.js';
|
|
@@ -134,10 +134,10 @@ export const register_action_ws = (options) => {
|
|
|
134
134
|
}
|
|
135
135
|
};
|
|
136
136
|
return {
|
|
137
|
-
onOpen: async (
|
|
137
|
+
onOpen: async (_event, ws) => {
|
|
138
138
|
const connection_id = transport.add_connection(ws, token_hash, account_id, api_token_id);
|
|
139
139
|
captured_connection_id = connection_id;
|
|
140
|
-
log.debug('ws opened', connection_id
|
|
140
|
+
log.debug('ws opened', connection_id);
|
|
141
141
|
if (heartbeat_enabled) {
|
|
142
142
|
last_receive_time = Date.now();
|
|
143
143
|
heartbeat_timer = setInterval(() => {
|
|
@@ -294,7 +294,14 @@ export const register_action_ws = (options) => {
|
|
|
294
294
|
ws.send(JSON.stringify({ jsonrpc: JSONRPC_VERSION, id, result: output }));
|
|
295
295
|
}
|
|
296
296
|
catch (error) {
|
|
297
|
-
|
|
297
|
+
if (error instanceof ThrownJsonrpcError) {
|
|
298
|
+
// Expected handler outcome (conflict, not_found, invalid_params, ...).
|
|
299
|
+
// Log at debug without the stack — the throw site is part of protocol, not a bug.
|
|
300
|
+
log.debug('handler error:', method, `${error.code} ${error.message}`);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
log.error('handler error:', method, error);
|
|
304
|
+
}
|
|
298
305
|
ws.send(JSON.stringify(create_jsonrpc_error_response_from_thrown(id, error)));
|
|
299
306
|
}
|
|
300
307
|
finally {
|
|
@@ -317,7 +324,7 @@ export const register_action_ws = (options) => {
|
|
|
317
324
|
}
|
|
318
325
|
}
|
|
319
326
|
transport.remove_connection(ws);
|
|
320
|
-
log.debug('ws closed', event);
|
|
327
|
+
log.debug('ws closed', captured_connection_id, { code: event.code, reason: event.reason });
|
|
321
328
|
},
|
|
322
329
|
};
|
|
323
330
|
}));
|
package/dist/dev/setup.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
11
|
import type { CommandDeps, CommandResult, EnvDeps, FsReadDeps, FsRemoveDeps, FsWriteDeps } from '../runtime/deps.js';
|
|
12
|
+
import type { QueryDeps } from '../db/query_deps.js';
|
|
12
13
|
/**
|
|
13
14
|
* Optional logger for setup helpers.
|
|
14
15
|
*
|
|
@@ -156,4 +157,37 @@ export declare const create_database: (deps: CommandDeps, db_name: string, optio
|
|
|
156
157
|
* @returns result describing what happened
|
|
157
158
|
*/
|
|
158
159
|
export declare const reset_database: (deps: CommandDeps & FsReadDeps & FsRemoveDeps, database_url: string, options?: ResetDatabaseOptions) => Promise<ResetDbResult>;
|
|
160
|
+
/** Input to `seed_dev_account`. */
|
|
161
|
+
export interface SeedDevAccountInput {
|
|
162
|
+
/** Account username. Policy is bypassed — any non-empty string is accepted. */
|
|
163
|
+
username: string;
|
|
164
|
+
/** Account password. Policy is bypassed — any non-empty string is accepted. */
|
|
165
|
+
password: string;
|
|
166
|
+
/** Roles to grant via permit (idempotent). */
|
|
167
|
+
roles?: ReadonlyArray<string>;
|
|
168
|
+
}
|
|
169
|
+
/** Result of `seed_dev_account`. */
|
|
170
|
+
export interface SeedDevAccountResult {
|
|
171
|
+
account_id: string;
|
|
172
|
+
actor_id: string;
|
|
173
|
+
/** True if a new account was created; false if one already existed. */
|
|
174
|
+
created: boolean;
|
|
175
|
+
}
|
|
176
|
+
/** Dependencies for `seed_dev_account`. */
|
|
177
|
+
export interface SeedDevAccountDeps extends QueryDeps {
|
|
178
|
+
/** Password hasher (e.g., `argon2_password_deps.hash_password`). */
|
|
179
|
+
hash_password: (password: string) => Promise<string>;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Seed a development test account, bypassing username/password policy.
|
|
183
|
+
*
|
|
184
|
+
* Idempotent by username — if an account with the given username already
|
|
185
|
+
* exists, reuses it and only reconciles the requested role grants. Never
|
|
186
|
+
* updates an existing password (rerun would silently rotate it).
|
|
187
|
+
*
|
|
188
|
+
* Intended for `scripts/dev_setup.ts` — do not call in production.
|
|
189
|
+
*/
|
|
190
|
+
export declare const seed_dev_account: (deps: SeedDevAccountDeps, input: SeedDevAccountInput, options?: {
|
|
191
|
+
log?: SetupLogger;
|
|
192
|
+
}) => Promise<SeedDevAccountResult>;
|
|
159
193
|
//# sourceMappingURL=setup.d.ts.map
|
package/dist/dev/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/dev/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACX,WAAW,EACX,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EACZ,WAAW,EACX,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/dev/setup.ts"],"names":[],"mappings":"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"}
|
package/dist/dev/setup.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
+
import { query_account_by_username, query_actor_by_account, query_create_account_with_actor, } from '../auth/account_queries.js';
|
|
12
|
+
import { query_grant_permit } from '../auth/permit_queries.js';
|
|
11
13
|
/** Default logger using bracket format. */
|
|
12
14
|
export const default_setup_logger = {
|
|
13
15
|
ok: (msg) => console.log(` [ok] ${msg}`),
|
|
@@ -263,3 +265,49 @@ export const reset_database = async (deps, database_url, options) => {
|
|
|
263
265
|
log.ok(`Created database: ${db_name}`);
|
|
264
266
|
return { reset: true, skipped: false, db_type: 'postgres' };
|
|
265
267
|
};
|
|
268
|
+
/**
|
|
269
|
+
* Seed a development test account, bypassing username/password policy.
|
|
270
|
+
*
|
|
271
|
+
* Idempotent by username — if an account with the given username already
|
|
272
|
+
* exists, reuses it and only reconciles the requested role grants. Never
|
|
273
|
+
* updates an existing password (rerun would silently rotate it).
|
|
274
|
+
*
|
|
275
|
+
* Intended for `scripts/dev_setup.ts` — do not call in production.
|
|
276
|
+
*/
|
|
277
|
+
export const seed_dev_account = async (deps, input, options) => {
|
|
278
|
+
const log = options?.log ?? default_setup_logger;
|
|
279
|
+
const query_deps = { db: deps.db };
|
|
280
|
+
const existing = await query_account_by_username(query_deps, input.username);
|
|
281
|
+
if (existing) {
|
|
282
|
+
const actor = await query_actor_by_account(query_deps, existing.id);
|
|
283
|
+
if (!actor) {
|
|
284
|
+
log.error(`dev account '${input.username}' exists but has no actor`);
|
|
285
|
+
throw new Error(`dev account '${input.username}' has no actor`);
|
|
286
|
+
}
|
|
287
|
+
for (const role of input.roles ?? []) {
|
|
288
|
+
await query_grant_permit(query_deps, {
|
|
289
|
+
actor_id: actor.id,
|
|
290
|
+
role,
|
|
291
|
+
granted_by: null,
|
|
292
|
+
expires_at: null,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
log.skip(`Dev account '${input.username}' already exists`);
|
|
296
|
+
return { account_id: existing.id, actor_id: actor.id, created: false };
|
|
297
|
+
}
|
|
298
|
+
const password_hash = await deps.hash_password(input.password);
|
|
299
|
+
const { account, actor } = await query_create_account_with_actor(query_deps, {
|
|
300
|
+
username: input.username,
|
|
301
|
+
password_hash,
|
|
302
|
+
});
|
|
303
|
+
for (const role of input.roles ?? []) {
|
|
304
|
+
await query_grant_permit(query_deps, {
|
|
305
|
+
actor_id: actor.id,
|
|
306
|
+
role,
|
|
307
|
+
granted_by: null,
|
|
308
|
+
expires_at: null,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
log.ok(`Seeded dev account '${input.username}'`);
|
|
312
|
+
return { account_id: account.id, actor_id: actor.id, created: true };
|
|
313
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"deno.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deno.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAwDtE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,WA4HhE,CAAC"}
|
package/dist/runtime/deno.js
CHANGED
|
@@ -38,6 +38,33 @@ export const create_deno_runtime = (args) => ({
|
|
|
38
38
|
mkdir: (path, options) => Deno.mkdir(path, options),
|
|
39
39
|
read_text_file: (path) => Deno.readTextFile(path),
|
|
40
40
|
read_file: (path) => Deno.readFile(path),
|
|
41
|
+
read_text_from_offset: async (path, offset) => {
|
|
42
|
+
const s = await Deno.stat(path);
|
|
43
|
+
const file_size = s.size;
|
|
44
|
+
const bytes_to_read = Math.max(0, file_size - offset);
|
|
45
|
+
if (bytes_to_read === 0)
|
|
46
|
+
return { content: '', bytes_read: 0, file_size };
|
|
47
|
+
const handle = await Deno.open(path, { read: true });
|
|
48
|
+
try {
|
|
49
|
+
await handle.seek(offset, Deno.SeekMode.Start);
|
|
50
|
+
const buffer = new Uint8Array(bytes_to_read);
|
|
51
|
+
const bytes_read = (await handle.read(buffer)) ?? 0;
|
|
52
|
+
return {
|
|
53
|
+
content: new TextDecoder().decode(buffer.subarray(0, bytes_read)),
|
|
54
|
+
bytes_read,
|
|
55
|
+
file_size,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
handle.close();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
readdir: async (path) => {
|
|
63
|
+
const names = [];
|
|
64
|
+
for await (const entry of Deno.readDir(path))
|
|
65
|
+
names.push(entry.name);
|
|
66
|
+
return names;
|
|
67
|
+
},
|
|
41
68
|
write_text_file: (path, content) => Deno.writeTextFile(path, content),
|
|
42
69
|
write_file: (path, data) => Deno.writeFile(path, data),
|
|
43
70
|
rename: (old_path, new_path) => Deno.rename(old_path, new_path),
|
|
@@ -45,20 +72,46 @@ export const create_deno_runtime = (args) => ({
|
|
|
45
72
|
// === HTTP ===
|
|
46
73
|
fetch: globalThis.fetch,
|
|
47
74
|
// === Local Commands ===
|
|
48
|
-
run_command: async (cmd, args) => {
|
|
75
|
+
run_command: async (cmd, args, options) => {
|
|
49
76
|
try {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
const controller = options?.timeout_ms !== undefined ? new AbortController() : null;
|
|
78
|
+
const signal = controller && options?.signal
|
|
79
|
+
? AbortSignal.any([controller.signal, options.signal])
|
|
80
|
+
: (controller?.signal ?? options?.signal);
|
|
81
|
+
const timer = controller && options?.timeout_ms !== undefined
|
|
82
|
+
? setTimeout(() => controller.abort(), options.timeout_ms)
|
|
83
|
+
: null;
|
|
84
|
+
let timed_out = false;
|
|
85
|
+
if (controller) {
|
|
86
|
+
controller.signal.addEventListener('abort', () => {
|
|
87
|
+
if (options?.signal?.aborted)
|
|
88
|
+
return;
|
|
89
|
+
timed_out = true;
|
|
90
|
+
}, { once: true });
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const proc = new Deno.Command(cmd, {
|
|
94
|
+
args,
|
|
95
|
+
cwd: options?.cwd,
|
|
96
|
+
signal,
|
|
97
|
+
stdout: 'piped',
|
|
98
|
+
stderr: 'piped',
|
|
99
|
+
});
|
|
100
|
+
const result = await proc.output();
|
|
101
|
+
const base = {
|
|
102
|
+
success: result.code === 0 && !timed_out,
|
|
103
|
+
code: result.code,
|
|
104
|
+
stdout: new TextDecoder().decode(result.stdout),
|
|
105
|
+
stderr: new TextDecoder().decode(result.stderr),
|
|
106
|
+
};
|
|
107
|
+
if (options?.timeout_ms !== undefined)
|
|
108
|
+
base.timed_out = timed_out;
|
|
109
|
+
return base;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (timer !== null)
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
}
|
|
62
115
|
}
|
|
63
116
|
catch (error) {
|
|
64
117
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/runtime/deps.d.ts
CHANGED
|
@@ -16,12 +16,28 @@ export interface StatResult {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Result of executing a command.
|
|
19
|
+
*
|
|
20
|
+
* `timed_out` is present only when `timeout_ms` was passed in `RunCommandOptions`
|
|
21
|
+
* and the process was killed after exceeding the timeout. Callers that pass
|
|
22
|
+
* `timeout_ms` should check this flag to distinguish timeout from exit-code failure.
|
|
19
23
|
*/
|
|
20
24
|
export interface CommandResult {
|
|
21
25
|
success: boolean;
|
|
22
26
|
code: number;
|
|
23
27
|
stdout: string;
|
|
24
28
|
stderr: string;
|
|
29
|
+
timed_out?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Options for `run_command`.
|
|
33
|
+
*/
|
|
34
|
+
export interface RunCommandOptions {
|
|
35
|
+
/** Working directory for the child process. */
|
|
36
|
+
cwd?: string;
|
|
37
|
+
/** AbortSignal to terminate the child process. */
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
/** Kill the process and return `timed_out: true` after this many milliseconds. */
|
|
40
|
+
timeout_ms?: number;
|
|
25
41
|
}
|
|
26
42
|
/**
|
|
27
43
|
* Environment variable access.
|
|
@@ -32,16 +48,37 @@ export interface EnvDeps {
|
|
|
32
48
|
/** Set an environment variable. */
|
|
33
49
|
env_set: (name: string, value: string) => void;
|
|
34
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Result of reading text from a byte offset.
|
|
53
|
+
*/
|
|
54
|
+
export interface ReadTextFromOffsetResult {
|
|
55
|
+
/** Decoded text content read from the offset. */
|
|
56
|
+
content: string;
|
|
57
|
+
/** Number of bytes actually read. */
|
|
58
|
+
bytes_read: number;
|
|
59
|
+
/** Total file size at the time of the read (for truncation detection). */
|
|
60
|
+
file_size: number;
|
|
61
|
+
}
|
|
35
62
|
/**
|
|
36
63
|
* File system read operations.
|
|
37
64
|
*/
|
|
38
65
|
export interface FsReadDeps {
|
|
39
66
|
/** Get file/directory stats, or null if path doesn't exist. */
|
|
40
67
|
stat: (path: string) => Promise<StatResult | null>;
|
|
41
|
-
/** Read a file as text. */
|
|
68
|
+
/** Read a file as text. Throws if the file does not exist. */
|
|
42
69
|
read_text_file: (path: string) => Promise<string>;
|
|
43
|
-
/** Read a file as bytes. */
|
|
70
|
+
/** Read a file as bytes. Throws if the file does not exist. */
|
|
44
71
|
read_file: (path: string) => Promise<Uint8Array>;
|
|
72
|
+
/**
|
|
73
|
+
* Read text starting from a byte offset. Throws if the file does not exist.
|
|
74
|
+
*
|
|
75
|
+
* Returns `content`, `bytes_read`, and `file_size` so callers can detect
|
|
76
|
+
* truncation (when `file_size < offset`) and tail incrementally without
|
|
77
|
+
* re-reading the whole file.
|
|
78
|
+
*/
|
|
79
|
+
read_text_from_offset: (path: string, offset: number) => Promise<ReadTextFromOffsetResult>;
|
|
80
|
+
/** List directory entries (names, not full paths). Throws if the directory does not exist. */
|
|
81
|
+
readdir: (path: string) => Promise<Array<string>>;
|
|
45
82
|
}
|
|
46
83
|
/**
|
|
47
84
|
* File system write operations.
|
|
@@ -71,8 +108,15 @@ export interface FsRemoveDeps {
|
|
|
71
108
|
* Command execution.
|
|
72
109
|
*/
|
|
73
110
|
export interface CommandDeps {
|
|
74
|
-
/**
|
|
75
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Run a command and return the result. Never throws — failures surface as
|
|
113
|
+
* `success: false`.
|
|
114
|
+
*
|
|
115
|
+
* `options.cwd` sets the child's working directory. `options.signal` aborts
|
|
116
|
+
* the child when the signal fires. `options.timeout_ms` kills the child
|
|
117
|
+
* after the given duration and returns `timed_out: true` on the result.
|
|
118
|
+
*/
|
|
119
|
+
run_command: (cmd: string, args: Array<string>, options?: RunCommandOptions) => Promise<CommandResult>;
|
|
76
120
|
}
|
|
77
121
|
/**
|
|
78
122
|
* HTTP fetch capability.
|
|
@@ -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;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/mock.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import type { RuntimeDeps, CommandResult } from './deps.js';
|
|
10
|
+
import type { RuntimeDeps, CommandResult, RunCommandOptions } from './deps.js';
|
|
11
11
|
/**
|
|
12
12
|
* Mock `RuntimeDeps` with observable state for assertions.
|
|
13
13
|
*/
|
|
@@ -22,10 +22,11 @@ export interface MockRuntime extends RuntimeDeps {
|
|
|
22
22
|
mock_dirs: Set<string>;
|
|
23
23
|
/** Exit calls recorded (exit codes). */
|
|
24
24
|
exit_calls: Array<number>;
|
|
25
|
-
/** Commands executed. */
|
|
25
|
+
/** Commands executed. Captures `options` when passed so tests can assert cwd/timeout/signal. */
|
|
26
26
|
command_calls: Array<{
|
|
27
27
|
cmd: string;
|
|
28
28
|
args: Array<string>;
|
|
29
|
+
options?: RunCommandOptions;
|
|
29
30
|
}>;
|
|
30
31
|
/** Commands executed with inherit. */
|
|
31
32
|
command_inherit_calls: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/mock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAc,aAAa,EAAC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/mock.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAc,aAAa,EAAE,iBAAiB,EAAC,MAAM,WAAW,CAAC;AAIzF;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC/C,kCAAkC;IAClC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,0CAA0C;IAC1C,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,+CAA+C;IAC/C,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACvC,mCAAmC;IACnC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,wCAAwC;IACxC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,gGAAgG;IAChG,aAAa,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;KAAC,CAAC,CAAC;IACtF,sCAAsC;IACtC,qBAAqB,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;KAAC,CAAC,CAAC;IACjE,8BAA8B;IAC9B,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,4CAA4C;IAC5C,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjD,yCAAyC;IACzC,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,4BAA4B;IAC5B,WAAW,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,WAAW,CAAA;KAAC,CAAC,CAAC;IACxE,wDAAwD;IACxD,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAM,KAAK,CAAC,MAAM,CAAM,KAAG,WAkO9D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,WAAW,KAAG,IAazD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,EAAE,OAAO,MAAM,KAAG,IAEpE,CAAC;AAEF;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;CAKxB"}
|
package/dist/runtime/mock.js
CHANGED
|
@@ -114,6 +114,55 @@ export const create_mock_runtime = (args = []) => {
|
|
|
114
114
|
error.code = 'ENOENT';
|
|
115
115
|
throw error;
|
|
116
116
|
},
|
|
117
|
+
read_text_from_offset: async (path, offset) => {
|
|
118
|
+
let bytes;
|
|
119
|
+
const stored_bytes = mock_fs_bytes.get(path);
|
|
120
|
+
if (stored_bytes !== undefined) {
|
|
121
|
+
bytes = stored_bytes;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const content = mock_fs.get(path);
|
|
125
|
+
if (content === undefined) {
|
|
126
|
+
const error = new Error(`ENOENT: no such file or directory: ${path}`);
|
|
127
|
+
error.code = 'ENOENT';
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
bytes = new TextEncoder().encode(content);
|
|
131
|
+
}
|
|
132
|
+
const file_size = bytes.length;
|
|
133
|
+
const bytes_to_read = Math.max(0, file_size - offset);
|
|
134
|
+
if (bytes_to_read === 0)
|
|
135
|
+
return { content: '', bytes_read: 0, file_size };
|
|
136
|
+
const slice = bytes.subarray(offset, offset + bytes_to_read);
|
|
137
|
+
return {
|
|
138
|
+
content: new TextDecoder().decode(slice),
|
|
139
|
+
bytes_read: slice.length,
|
|
140
|
+
file_size,
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
readdir: async (path) => {
|
|
144
|
+
const prefix = path.endsWith('/') ? path : path + '/';
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
const collect = (key) => {
|
|
147
|
+
if (!key.startsWith(prefix))
|
|
148
|
+
return;
|
|
149
|
+
const rest = key.slice(prefix.length);
|
|
150
|
+
const slash = rest.indexOf('/');
|
|
151
|
+
seen.add(slash === -1 ? rest : rest.slice(0, slash));
|
|
152
|
+
};
|
|
153
|
+
for (const key of mock_fs.keys())
|
|
154
|
+
collect(key);
|
|
155
|
+
for (const key of mock_fs_bytes.keys())
|
|
156
|
+
collect(key);
|
|
157
|
+
for (const key of mock_dirs)
|
|
158
|
+
collect(key);
|
|
159
|
+
if (seen.size === 0 && !mock_dirs.has(path)) {
|
|
160
|
+
const error = new Error(`ENOENT: no such file or directory: ${path}`);
|
|
161
|
+
error.code = 'ENOENT';
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return Array.from(seen).sort();
|
|
165
|
+
},
|
|
117
166
|
write_text_file: async (path, content) => {
|
|
118
167
|
mock_fs.set(path, content);
|
|
119
168
|
},
|
|
@@ -163,13 +212,20 @@ export const create_mock_runtime = (args = []) => {
|
|
|
163
212
|
throw new TypeError(`fetch failed (no mock for ${url})`);
|
|
164
213
|
},
|
|
165
214
|
// === Local Commands ===
|
|
166
|
-
run_command: async (cmd, args) => {
|
|
167
|
-
command_calls.push({ cmd, args });
|
|
215
|
+
run_command: async (cmd, args, options) => {
|
|
216
|
+
command_calls.push(options ? { cmd, args, options } : { cmd, args });
|
|
168
217
|
const key = `${cmd} ${args.join(' ')}`;
|
|
169
218
|
const mocked = mock_command_results.get(key);
|
|
170
|
-
if (mocked)
|
|
219
|
+
if (mocked) {
|
|
220
|
+
if (options?.timeout_ms !== undefined && mocked.timed_out === undefined) {
|
|
221
|
+
return { ...mocked, timed_out: false };
|
|
222
|
+
}
|
|
171
223
|
return mocked;
|
|
172
|
-
|
|
224
|
+
}
|
|
225
|
+
const result = { success: true, code: 0, stdout: '', stderr: '' };
|
|
226
|
+
if (options?.timeout_ms !== undefined)
|
|
227
|
+
result.timed_out = false;
|
|
228
|
+
return result;
|
|
173
229
|
},
|
|
174
230
|
run_command_inherit: async (cmd, args) => {
|
|
175
231
|
command_inherit_calls.push({ cmd, args });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,OAAM,aAAa,CAAC,MAAM,CAAyB,KACjD,
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,OAAM,aAAa,CAAC,MAAM,CAAyB,KACjD,WAmKD,CAAC"}
|
package/dist/runtime/node.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Buffer } from 'node:buffer';
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
|
-
import { stat, mkdir, readFile, writeFile, rename, rm } from 'node:fs/promises';
|
|
11
|
+
import { stat, mkdir, readFile, readdir, writeFile, rename, rm, open } from 'node:fs/promises';
|
|
12
12
|
import process from 'node:process';
|
|
13
13
|
/**
|
|
14
14
|
* Create a `RuntimeDeps` backed by Node.js APIs.
|
|
@@ -42,6 +42,27 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
42
42
|
},
|
|
43
43
|
read_text_file: (path) => readFile(path, 'utf-8'),
|
|
44
44
|
read_file: (path) => readFile(path).then((buf) => new Uint8Array(buf)),
|
|
45
|
+
read_text_from_offset: async (path, offset) => {
|
|
46
|
+
const s = await stat(path);
|
|
47
|
+
const file_size = s.size;
|
|
48
|
+
const bytes_to_read = Math.max(0, file_size - offset);
|
|
49
|
+
if (bytes_to_read === 0)
|
|
50
|
+
return { content: '', bytes_read: 0, file_size };
|
|
51
|
+
const handle = await open(path, 'r');
|
|
52
|
+
try {
|
|
53
|
+
const buffer = Buffer.alloc(bytes_to_read);
|
|
54
|
+
const { bytesRead } = await handle.read(buffer, 0, bytes_to_read, offset);
|
|
55
|
+
return {
|
|
56
|
+
content: buffer.toString('utf-8', 0, bytesRead),
|
|
57
|
+
bytes_read: bytesRead,
|
|
58
|
+
file_size,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await handle.close();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
readdir: (path) => readdir(path),
|
|
45
66
|
write_text_file: (path, content) => writeFile(path, content, 'utf-8'),
|
|
46
67
|
write_file: (path, data) => writeFile(path, data),
|
|
47
68
|
rename: (old_path, new_path) => rename(old_path, new_path),
|
|
@@ -49,17 +70,45 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
49
70
|
// === HTTP ===
|
|
50
71
|
fetch: globalThis.fetch,
|
|
51
72
|
// === Local Commands ===
|
|
52
|
-
run_command: (cmd, args) => {
|
|
73
|
+
run_command: (cmd, args, options) => {
|
|
53
74
|
return new Promise((resolve) => {
|
|
54
75
|
const proc = spawn(cmd, args, {
|
|
55
76
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
77
|
+
cwd: options?.cwd,
|
|
56
78
|
});
|
|
57
79
|
const stdout_chunks = [];
|
|
58
80
|
const stderr_chunks = [];
|
|
81
|
+
let timed_out = false;
|
|
82
|
+
let done = false;
|
|
83
|
+
const finish = (result) => {
|
|
84
|
+
if (done)
|
|
85
|
+
return;
|
|
86
|
+
done = true;
|
|
87
|
+
if (timer !== null)
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
if (options?.signal)
|
|
90
|
+
options.signal.removeEventListener('abort', on_abort);
|
|
91
|
+
resolve(result);
|
|
92
|
+
};
|
|
93
|
+
const on_abort = () => {
|
|
94
|
+
proc.kill();
|
|
95
|
+
};
|
|
96
|
+
const timer = options?.timeout_ms !== undefined
|
|
97
|
+
? setTimeout(() => {
|
|
98
|
+
timed_out = true;
|
|
99
|
+
proc.kill();
|
|
100
|
+
}, options.timeout_ms)
|
|
101
|
+
: null;
|
|
102
|
+
if (options?.signal) {
|
|
103
|
+
if (options.signal.aborted)
|
|
104
|
+
proc.kill();
|
|
105
|
+
else
|
|
106
|
+
options.signal.addEventListener('abort', on_abort, { once: true });
|
|
107
|
+
}
|
|
59
108
|
proc.stdout.on('data', (chunk) => stdout_chunks.push(chunk));
|
|
60
109
|
proc.stderr.on('data', (chunk) => stderr_chunks.push(chunk));
|
|
61
110
|
proc.on('error', (error) => {
|
|
62
|
-
|
|
111
|
+
finish({
|
|
63
112
|
success: false,
|
|
64
113
|
code: 1,
|
|
65
114
|
stdout: '',
|
|
@@ -67,12 +116,15 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
67
116
|
});
|
|
68
117
|
});
|
|
69
118
|
proc.on('close', (code) => {
|
|
70
|
-
|
|
71
|
-
success: code === 0,
|
|
119
|
+
const result = {
|
|
120
|
+
success: code === 0 && !timed_out,
|
|
72
121
|
code: code ?? 1,
|
|
73
122
|
stdout: Buffer.concat(stdout_chunks).toString('utf-8').trim(),
|
|
74
123
|
stderr: Buffer.concat(stderr_chunks).toString('utf-8').trim(),
|
|
75
|
-
}
|
|
124
|
+
};
|
|
125
|
+
if (options?.timeout_ms !== undefined)
|
|
126
|
+
result.timed_out = timed_out;
|
|
127
|
+
finish(result);
|
|
76
128
|
});
|
|
77
129
|
});
|
|
78
130
|
},
|