@fuzdev/fuz_app 0.72.1 → 0.74.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/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +8 -1
- package/dist/runtime/deps.d.ts +38 -1
- package/dist/runtime/deps.d.ts.map +1 -1
- package/dist/runtime/mock.d.ts.map +1 -1
- package/dist/runtime/mock.js +53 -3
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +17 -1
- package/dist/testing/rpc_round_trip.d.ts +4 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +28 -2
- package/package.json +7 -6
|
@@ -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;AA0DtE;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,WAmIhE,CAAC"}
|
package/dist/runtime/deno.js
CHANGED
|
@@ -29,7 +29,7 @@ export const create_deno_runtime = (args) => ({
|
|
|
29
29
|
stat: async (path) => {
|
|
30
30
|
try {
|
|
31
31
|
const s = await Deno.stat(path);
|
|
32
|
-
return { is_file: s.isFile, is_directory: s.isDirectory };
|
|
32
|
+
return { is_file: s.isFile, is_directory: s.isDirectory, size: s.size };
|
|
33
33
|
}
|
|
34
34
|
catch {
|
|
35
35
|
return null;
|
|
@@ -38,6 +38,13 @@ 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_file_stream: async (path) => (await Deno.open(path, { read: true })).readable,
|
|
42
|
+
write_file_stream: async (path, data) => {
|
|
43
|
+
const file = await Deno.open(path, { write: true, create: true, truncate: true });
|
|
44
|
+
// `pipeTo` closes the writable (and so the underlying file) on completion
|
|
45
|
+
// and aborts it on error — no manual `close()` needed.
|
|
46
|
+
await data.pipeTo(file.writable);
|
|
47
|
+
},
|
|
41
48
|
read_text_from_offset: async (path, offset) => {
|
|
42
49
|
const s = await Deno.stat(path);
|
|
43
50
|
const file_size = s.size;
|
package/dist/runtime/deps.d.ts
CHANGED
|
@@ -13,6 +13,17 @@
|
|
|
13
13
|
export interface StatResult {
|
|
14
14
|
is_file: boolean;
|
|
15
15
|
is_directory: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Byte length of a regular file. Meaningful only when `is_file` is true; for
|
|
18
|
+
* directories it is runtime-defined (real OS `stat` reports the directory
|
|
19
|
+
* entry's on-disk size, not 0 — only `create_mock_runtime` reports 0).
|
|
20
|
+
* Populated by every runtime factory (`create_node_runtime` /
|
|
21
|
+
* `create_deno_runtime` / `create_mock_runtime`); optional so loose test
|
|
22
|
+
* stubs that only assert `is_file` / `is_directory` don't have to supply it.
|
|
23
|
+
* Callers that need an exact size (e.g. a streaming upload's
|
|
24
|
+
* `Content-Length`) read it from a real runtime, where it is always present.
|
|
25
|
+
*/
|
|
26
|
+
size?: number;
|
|
16
27
|
}
|
|
17
28
|
/**
|
|
18
29
|
* Result of executing a command.
|
|
@@ -95,6 +106,32 @@ export interface FsWriteDeps {
|
|
|
95
106
|
/** Rename (move) a file. */
|
|
96
107
|
rename: (old_path: string, new_path: string) => Promise<void>;
|
|
97
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Streaming file I/O — read a file as a byte stream, or write a byte stream to
|
|
111
|
+
* a file, both bounded in memory (peak ≈ one chunk, not the whole file).
|
|
112
|
+
*
|
|
113
|
+
* Kept separate from `FsReadDeps` / `FsWriteDeps` so the whole-buffer
|
|
114
|
+
* `read_file` / `write_file` consumers and their partial test stubs are
|
|
115
|
+
* unaffected; only the full runtime factories implement these. Used for
|
|
116
|
+
* GB-scale artifact transfer (the `fuzf file get` / `put` path) where buffering
|
|
117
|
+
* the whole file would OOM the client.
|
|
118
|
+
*/
|
|
119
|
+
export interface FsStreamDeps {
|
|
120
|
+
/**
|
|
121
|
+
* Open a file as a `ReadableStream` of its bytes — read incrementally, so
|
|
122
|
+
* peak memory is one chunk rather than the whole file. Throws if the file
|
|
123
|
+
* does not exist. Use as a streaming upload body or for an incremental hash
|
|
124
|
+
* pass over a large file.
|
|
125
|
+
*/
|
|
126
|
+
read_file_stream: (path: string) => Promise<ReadableStream<Uint8Array>>;
|
|
127
|
+
/**
|
|
128
|
+
* Write a `ReadableStream` of bytes to a file, consuming it with
|
|
129
|
+
* backpressure (peak memory is one chunk). Creates or truncates `path`.
|
|
130
|
+
* Throws on any I/O error; a partially-written file may remain, so callers
|
|
131
|
+
* needing atomicity write to a temp path then `rename`.
|
|
132
|
+
*/
|
|
133
|
+
write_file_stream: (path: string, data: ReadableStream<Uint8Array>) => Promise<void>;
|
|
134
|
+
}
|
|
98
135
|
/**
|
|
99
136
|
* File system remove operations.
|
|
100
137
|
*/
|
|
@@ -155,7 +192,7 @@ export interface ProcessDeps {
|
|
|
155
192
|
* Functions should accept narrow `*Deps` interfaces, not this full type —
|
|
156
193
|
* this type is for the wiring layer that creates and passes the runtime.
|
|
157
194
|
*/
|
|
158
|
-
export interface RuntimeDeps extends EnvDeps, FsReadDeps, FsWriteDeps, FsRemoveDeps, CommandDeps, FetchDeps, TerminalDeps, ProcessDeps, LogDeps {
|
|
195
|
+
export interface RuntimeDeps extends EnvDeps, FsReadDeps, FsWriteDeps, FsStreamDeps, FsRemoveDeps, CommandDeps, FetchDeps, TerminalDeps, ProcessDeps, LogDeps {
|
|
159
196
|
/** Get all environment variables. */
|
|
160
197
|
env_all: () => Record<string, string>;
|
|
161
198
|
/** CLI arguments passed to the program. */
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/deps.ts"],"names":[],"mappings":"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"}
|
|
@@ -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,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;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAM,KAAK,CAAC,MAAM,CAAM,KAAG,
|
|
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;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAM,KAAK,CAAC,MAAM,CAAM,KAAG,WAoR9D,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
|
@@ -74,11 +74,16 @@ export const create_mock_runtime = (args = []) => {
|
|
|
74
74
|
},
|
|
75
75
|
// === Local File System ===
|
|
76
76
|
stat: async (path) => {
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
const bytes = mock_fs_bytes.get(path);
|
|
78
|
+
if (bytes !== undefined) {
|
|
79
|
+
return { is_file: true, is_directory: false, size: bytes.length };
|
|
80
|
+
}
|
|
81
|
+
const text = mock_fs.get(path);
|
|
82
|
+
if (text !== undefined) {
|
|
83
|
+
return { is_file: true, is_directory: false, size: new TextEncoder().encode(text).length };
|
|
79
84
|
}
|
|
80
85
|
if (mock_dirs.has(path)) {
|
|
81
|
-
return { is_file: false, is_directory: true };
|
|
86
|
+
return { is_file: false, is_directory: true, size: 0 };
|
|
82
87
|
}
|
|
83
88
|
return null;
|
|
84
89
|
},
|
|
@@ -117,6 +122,28 @@ export const create_mock_runtime = (args = []) => {
|
|
|
117
122
|
error.code = 'ENOENT';
|
|
118
123
|
throw error;
|
|
119
124
|
},
|
|
125
|
+
read_file_stream: async (path) => {
|
|
126
|
+
let bytes = mock_fs_bytes.get(path);
|
|
127
|
+
if (bytes === undefined) {
|
|
128
|
+
const content = mock_fs.get(path);
|
|
129
|
+
if (content !== undefined)
|
|
130
|
+
bytes = new TextEncoder().encode(content);
|
|
131
|
+
}
|
|
132
|
+
if (bytes === undefined) {
|
|
133
|
+
const error = new Error(`ENOENT: no such file or directory: ${path}`);
|
|
134
|
+
error.code = 'ENOENT';
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
// Single-chunk stream — the mock buffers in memory (fine for tests);
|
|
138
|
+
// real runtimes stream incrementally.
|
|
139
|
+
const data = bytes;
|
|
140
|
+
return new ReadableStream({
|
|
141
|
+
start(controller) {
|
|
142
|
+
controller.enqueue(data);
|
|
143
|
+
controller.close();
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
},
|
|
120
147
|
read_text_from_offset: async (path, offset) => {
|
|
121
148
|
let bytes;
|
|
122
149
|
const stored_bytes = mock_fs_bytes.get(path);
|
|
@@ -172,6 +199,29 @@ export const create_mock_runtime = (args = []) => {
|
|
|
172
199
|
write_file: async (path, data) => {
|
|
173
200
|
mock_fs_bytes.set(path, data);
|
|
174
201
|
},
|
|
202
|
+
write_file_stream: async (path, data) => {
|
|
203
|
+
// Drain the stream into one buffer (the mock has no real disk;
|
|
204
|
+
// real runtimes write incrementally with backpressure).
|
|
205
|
+
const chunks = [];
|
|
206
|
+
let total = 0;
|
|
207
|
+
const reader = data.getReader();
|
|
208
|
+
for (;;) {
|
|
209
|
+
const { done, value } = await reader.read();
|
|
210
|
+
if (done)
|
|
211
|
+
break;
|
|
212
|
+
if (value) {
|
|
213
|
+
chunks.push(value);
|
|
214
|
+
total += value.length;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const merged = new Uint8Array(total);
|
|
218
|
+
let offset = 0;
|
|
219
|
+
for (const chunk of chunks) {
|
|
220
|
+
merged.set(chunk, offset);
|
|
221
|
+
offset += chunk.length;
|
|
222
|
+
}
|
|
223
|
+
mock_fs_bytes.set(path, merged);
|
|
224
|
+
},
|
|
175
225
|
rename: async (old_path, new_path) => {
|
|
176
226
|
const content = mock_fs.get(old_path);
|
|
177
227
|
if (content !== undefined) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/runtime/node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,OAAO,KAAK,EAAC,WAAW,EAA4B,MAAM,WAAW,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC/B,OAAM,aAAa,CAAC,MAAM,CAAyB,KACjD,WAmLD,CAAC"}
|
package/dist/runtime/node.js
CHANGED
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Buffer } from 'node:buffer';
|
|
10
10
|
import { spawn } from 'node:child_process';
|
|
11
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
11
12
|
import { stat, mkdir, readFile, readdir, writeFile, rename, rm, open } from 'node:fs/promises';
|
|
12
13
|
import process from 'node:process';
|
|
14
|
+
import { Readable } from 'node:stream';
|
|
15
|
+
import { pipeline } from 'node:stream/promises';
|
|
13
16
|
/**
|
|
14
17
|
* Create a `RuntimeDeps` backed by Node.js APIs.
|
|
15
18
|
*
|
|
@@ -31,7 +34,7 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
31
34
|
stat: async (path) => {
|
|
32
35
|
try {
|
|
33
36
|
const s = await stat(path);
|
|
34
|
-
return { is_file: s.isFile(), is_directory: s.isDirectory() };
|
|
37
|
+
return { is_file: s.isFile(), is_directory: s.isDirectory(), size: s.size };
|
|
35
38
|
}
|
|
36
39
|
catch {
|
|
37
40
|
return null;
|
|
@@ -42,6 +45,19 @@ export const create_node_runtime = (args = process.argv.slice(2)) => ({
|
|
|
42
45
|
},
|
|
43
46
|
read_text_file: (path) => readFile(path, 'utf-8'),
|
|
44
47
|
read_file: (path) => readFile(path).then((buf) => new Uint8Array(buf)),
|
|
48
|
+
// `Readable.toWeb` / `fromWeb` bridge Node streams to the web stream shape
|
|
49
|
+
// the interface speaks. The casts cross Node's `stream/web` ReadableStream
|
|
50
|
+
// and the global DOM `ReadableStream` (structurally identical here).
|
|
51
|
+
read_file_stream: async (path) => {
|
|
52
|
+
// `createReadStream` is lazy — a missing file surfaces as a stream `error`
|
|
53
|
+
// event at consume time, not at the call. `stat` first so we honor the
|
|
54
|
+
// interface's eager-throw contract (matching Deno/mock).
|
|
55
|
+
await stat(path);
|
|
56
|
+
return Readable.toWeb(createReadStream(path));
|
|
57
|
+
},
|
|
58
|
+
write_file_stream: async (path, data) => {
|
|
59
|
+
await pipeline(Readable.fromWeb(data), createWriteStream(path));
|
|
60
|
+
},
|
|
45
61
|
read_text_from_offset: async (path, offset) => {
|
|
46
62
|
const s = await stat(path);
|
|
47
63
|
const file_size = s.size;
|
|
@@ -47,7 +47,10 @@ export interface RpcRoundTripTestOptions {
|
|
|
47
47
|
*
|
|
48
48
|
* Error responses (from missing DB state, etc.) are expected and validated
|
|
49
49
|
* as well-formed JSON-RPC errors. Successful responses are validated against
|
|
50
|
-
* `action.spec.output`.
|
|
50
|
+
* `action.spec.output`. A `method not found` (-32601) error is the one
|
|
51
|
+
* exception — it means the backend is missing a method the local surface
|
|
52
|
+
* advertises, so the round-trip fails loud (`assert_method_implemented`)
|
|
53
|
+
* rather than accepting it as a valid error envelope.
|
|
51
54
|
*/
|
|
52
55
|
export declare const describe_rpc_round_trip_tests: (options: RpcRoundTripTestOptions) => void;
|
|
53
56
|
//# sourceMappingURL=rpc_round_trip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAyB7B,OAAO,KAAK,EAAC,cAAc,EAAsB,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;;;OAKG;IACH,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,qDAAqD;IACrD,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AAoDD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,uBAAuB,KAAG,IAoHhF,CAAC"}
|
|
@@ -15,8 +15,9 @@ import './assert_dev_env.js';
|
|
|
15
15
|
*
|
|
16
16
|
* @module
|
|
17
17
|
*/
|
|
18
|
-
import { describe, test, beforeAll } from 'vitest';
|
|
18
|
+
import { describe, test, beforeAll, assert } from 'vitest';
|
|
19
19
|
import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
20
|
+
import { JSONRPC_METHOD_NOT_FOUND, JsonrpcErrorResponse } from '../http/jsonrpc.js';
|
|
20
21
|
import { generate_valid_body } from './schema_generators.js';
|
|
21
22
|
import { is_public_auth } from '../http/auth_shape.js';
|
|
22
23
|
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, assert_jsonrpc_success_response, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
|
|
@@ -41,6 +42,26 @@ const pick_rpc_auth_headers = (method, keeper, authed_account, admin_account) =>
|
|
|
41
42
|
}
|
|
42
43
|
return authed_account.create_session_headers();
|
|
43
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Guard against silent parity gaps: a method enumerated from the local action
|
|
47
|
+
* surface that the remote backend answers with `method not found` (-32601)
|
|
48
|
+
* means the backend is missing an implementation the local surface advertises.
|
|
49
|
+
* That is a well-formed JSON-RPC error, so the round-trip's
|
|
50
|
+
* `assert_jsonrpc_error_response` branch would otherwise accept it as a pass —
|
|
51
|
+
* masking missing methods across coequal backends. Fail loud instead, before
|
|
52
|
+
* the generic error-envelope acceptance runs.
|
|
53
|
+
*
|
|
54
|
+
* Only `JSONRPC_METHOD_NOT_FOUND` trips this — every other well-formed error
|
|
55
|
+
* (validation, `not_found` from missing DB state, auth denials) stays a valid
|
|
56
|
+
* round-trip outcome.
|
|
57
|
+
*/
|
|
58
|
+
const assert_method_implemented = (method, body) => {
|
|
59
|
+
const parsed = JsonrpcErrorResponse.safeParse(body);
|
|
60
|
+
if (parsed.success && parsed.data.error.code === JSONRPC_METHOD_NOT_FOUND) {
|
|
61
|
+
assert.fail(`method '${method}' is registered on the local surface but the backend` +
|
|
62
|
+
` returned method-not-found — backend is missing this method (parity gap)`);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
44
65
|
/**
|
|
45
66
|
* Run schema-driven round-trip validation for RPC endpoints.
|
|
46
67
|
*
|
|
@@ -53,7 +74,10 @@ const pick_rpc_auth_headers = (method, keeper, authed_account, admin_account) =>
|
|
|
53
74
|
*
|
|
54
75
|
* Error responses (from missing DB state, etc.) are expected and validated
|
|
55
76
|
* as well-formed JSON-RPC errors. Successful responses are validated against
|
|
56
|
-
* `action.spec.output`.
|
|
77
|
+
* `action.spec.output`. A `method not found` (-32601) error is the one
|
|
78
|
+
* exception — it means the backend is missing a method the local surface
|
|
79
|
+
* advertises, so the round-trip fails loud (`assert_method_implemented`)
|
|
80
|
+
* rather than accepting it as a valid error envelope.
|
|
57
81
|
*/
|
|
58
82
|
export const describe_rpc_round_trip_tests = (options) => {
|
|
59
83
|
const skip_set = new Set(options.skip_methods);
|
|
@@ -103,6 +127,7 @@ export const describe_rpc_round_trip_tests = (options) => {
|
|
|
103
127
|
assert_jsonrpc_success_response(body, action.spec.output);
|
|
104
128
|
}
|
|
105
129
|
else {
|
|
130
|
+
assert_method_implemented(action.spec.method, body);
|
|
106
131
|
assert_jsonrpc_error_response(body);
|
|
107
132
|
}
|
|
108
133
|
}
|
|
@@ -135,6 +160,7 @@ export const describe_rpc_round_trip_tests = (options) => {
|
|
|
135
160
|
assert_jsonrpc_success_response(body, action.spec.output);
|
|
136
161
|
}
|
|
137
162
|
else {
|
|
163
|
+
assert_method_implemented(action.spec.method, body);
|
|
138
164
|
assert_jsonrpc_error_response(body);
|
|
139
165
|
}
|
|
140
166
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.0",
|
|
4
4
|
"description": "fullstack app library",
|
|
5
5
|
"glyph": "🗝",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"@electric-sql/pglite": "^0.4.5",
|
|
66
66
|
"@fuzdev/blake3_wasm": "^0.1.0",
|
|
67
67
|
"@fuzdev/fuz_code": "^0.45.1",
|
|
68
|
-
"@fuzdev/fuz_css": "^0.
|
|
69
|
-
"@fuzdev/fuz_ui": "^0.
|
|
70
|
-
"@fuzdev/fuz_util": "^0.
|
|
68
|
+
"@fuzdev/fuz_css": "^0.60.0",
|
|
69
|
+
"@fuzdev/fuz_ui": "^0.195.1",
|
|
70
|
+
"@fuzdev/fuz_util": "^0.62.0",
|
|
71
71
|
"@fuzdev/gro": "^0.199.1",
|
|
72
72
|
"@hono/node-server": "^1.19.14",
|
|
73
73
|
"@hono/node-ws": "^1.3.1",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@ryanatkn/eslint-config": "^0.12.1",
|
|
77
77
|
"@sveltejs/acorn-typescript": "^1.0.9",
|
|
78
78
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
79
|
-
"@sveltejs/kit": "^2.
|
|
79
|
+
"@sveltejs/kit": "^2.61.1",
|
|
80
80
|
"@sveltejs/package": "^2.5.7",
|
|
81
81
|
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
82
82
|
"@types/estree": "^1.0.8",
|
|
@@ -92,8 +92,9 @@
|
|
|
92
92
|
"pg": "^8.20.0",
|
|
93
93
|
"prettier": "^3.7.4",
|
|
94
94
|
"prettier-plugin-svelte": "^3.5.1",
|
|
95
|
-
"svelte": "^5.
|
|
95
|
+
"svelte": "^5.56.0",
|
|
96
96
|
"svelte-check": "^4.4.5",
|
|
97
|
+
"svelte-docinfo": "^0.1.0",
|
|
97
98
|
"svelte2tsx": "^0.7.52",
|
|
98
99
|
"tslib": "^2.8.1",
|
|
99
100
|
"typescript": "^5.9.3",
|