houdini 2.0.0-next.27 → 2.0.0-next.29
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/build/cmd/init.js +108 -81
- package/build/lib/ast.d.ts +1 -1
- package/build/lib/codegen.d.ts +4 -4
- package/build/lib/codegen.js +136 -77
- package/build/lib/config.d.ts +2 -2
- package/build/lib/config.js +2 -1
- package/build/lib/constants.js +4 -0
- package/build/lib/database.d.ts +3 -3
- package/build/lib/database.js +60 -92
- package/build/lib/db.d.ts +13 -0
- package/build/lib/db.js +134 -0
- package/build/lib/fs.d.ts +6 -6
- package/build/lib/fs.js +4 -4
- package/build/lib/index.d.ts +1 -0
- package/build/lib/index.js +1 -0
- package/build/lib/logger.js +2 -2
- package/build/lib/parse.js +2 -0
- package/build/lib/plugins.d.ts +1 -1
- package/build/lib/plugins.js +13 -2
- package/build/lib/project.js +6 -2
- package/build/lib/schema.js +0 -1
- package/build/lib/walk.d.ts +7 -3
- package/build/node/index.js +28 -38
- package/build/package.json +29 -33
- package/build/router/conventions.d.ts +4 -4
- package/build/router/jwt.js +16 -31
- package/build/router/match.d.ts +1 -1
- package/build/router/match.js +2 -4
- package/build/router/session.js +1 -1
- package/build/runtime/cache/gc.js +1 -0
- package/build/runtime/cache/index.d.ts +2 -2
- package/build/runtime/cache/index.js +22 -1
- package/build/runtime/cache/lists.d.ts +5 -5
- package/build/runtime/cache/lists.js +9 -0
- package/build/runtime/cache/staleManager.js +25 -0
- package/build/runtime/cache/storage.js +4 -0
- package/build/runtime/cache/subscription.js +1 -0
- package/build/runtime/client.js +2 -0
- package/build/runtime/config.d.ts +1 -1
- package/build/runtime/deepEquals.js +2 -4
- package/build/runtime/documentStore.js +30 -4
- package/build/runtime/pagination.js +2 -1
- package/build/runtime/types.d.ts +1 -0
- package/build/runtime/types.js +10 -1
- package/build/vite/hmr.js +112 -89
- package/build/vite/houdini.js +1 -0
- package/build/vite/index.d.ts +2 -2
- package/build/vite/index.js +13 -5
- package/build/vite/schema.d.ts +1 -1
- package/build/vite/schema.js +70 -31
- package/package.json +33 -35
package/build/cmd/init.js
CHANGED
|
@@ -72,62 +72,99 @@ async function init(_path, args) {
|
|
|
72
72
|
let schemaPath = is_remote_endpoint ? "./schema.graphql" : "path/to/src/lib/**/*.graphql";
|
|
73
73
|
let pullSchema_content = null;
|
|
74
74
|
if (is_remote_endpoint && !args.yes) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
const { api_running } = await p.group(
|
|
76
|
+
{
|
|
77
|
+
api_running: () => p.confirm({
|
|
78
|
+
message: "Is your API currently running?",
|
|
79
|
+
initialValue: true
|
|
80
|
+
})
|
|
81
|
+
},
|
|
82
|
+
{ onCancel: () => pCancel() }
|
|
83
|
+
);
|
|
84
|
+
if (api_running) {
|
|
85
|
+
let number_of_round = 0;
|
|
86
|
+
let url_and_headers = "";
|
|
87
|
+
while (pullSchema_content === null && number_of_round < 10) {
|
|
88
|
+
number_of_round++;
|
|
89
|
+
const answer = await p.group(
|
|
90
|
+
{
|
|
91
|
+
url_and_headers: async () => p.text({
|
|
92
|
+
message: `What's the URL for your api? ${number_of_round === 1 ? "" : `(attempt ${number_of_round})`}`,
|
|
93
|
+
placeholder: `http://localhost:4000/graphql ${number_of_round === 1 ? "" : "Authorization=Bearer MyToken"}`,
|
|
94
|
+
validate: (value) => {
|
|
95
|
+
if (value && !value.startsWith("http")) {
|
|
96
|
+
return "Please enter a valid URL";
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
|
-
}
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
onCancel: () => pCancel()
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
url_and_headers = answer.url_and_headers;
|
|
106
|
+
const value_splited = url_and_headers.split(" ");
|
|
107
|
+
const local_url = value_splited[0];
|
|
108
|
+
const local_headers = value_splited.length > 1 ? extractHeadersStr(value_splited.slice(1).join(" ")) : headers;
|
|
109
|
+
const fetchTimeout = 3e4;
|
|
110
|
+
pullSchema_content = await pull_schema(
|
|
111
|
+
local_url,
|
|
112
|
+
fetchTimeout,
|
|
113
|
+
schemaPath,
|
|
114
|
+
local_headers,
|
|
115
|
+
true
|
|
116
|
+
);
|
|
117
|
+
if (pullSchema_content === null) {
|
|
118
|
+
const msg = `If you need to pass headers, add them after the URL (eg: '${green(
|
|
119
|
+
`http://myurl.com/graphql Authorization=Bearer MyToken`
|
|
120
|
+
)}')`;
|
|
121
|
+
p.log.error(msg);
|
|
122
|
+
}
|
|
123
|
+
url = url_and_headers === "" ? "http://localhost:4000/graphql" : local_url;
|
|
124
|
+
}
|
|
125
|
+
if (pullSchema_content === null) {
|
|
126
|
+
pCancel("We couldn't pull the schema. Please check your URL/headers and try again.");
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
const { has_schema_file } = await p.group(
|
|
130
|
+
{
|
|
131
|
+
has_schema_file: () => p.confirm({
|
|
132
|
+
message: "Do you have a schema file on disk we can use?",
|
|
133
|
+
initialValue: false
|
|
92
134
|
})
|
|
93
135
|
},
|
|
94
|
-
{
|
|
95
|
-
onCancel: () => pCancel()
|
|
96
|
-
}
|
|
97
|
-
);
|
|
98
|
-
url_and_headers = answer.url_and_headers;
|
|
99
|
-
const value_splited = url_and_headers.split(" ");
|
|
100
|
-
const local_url = value_splited[0];
|
|
101
|
-
const local_headers = value_splited.length > 1 ? extractHeadersStr(value_splited.slice(1).join(" ")) : headers;
|
|
102
|
-
const fetchTimeout = 3e4;
|
|
103
|
-
pullSchema_content = await pull_schema(
|
|
104
|
-
local_url,
|
|
105
|
-
fetchTimeout,
|
|
106
|
-
schemaPath,
|
|
107
|
-
local_headers,
|
|
108
|
-
true
|
|
136
|
+
{ onCancel: () => pCancel() }
|
|
109
137
|
);
|
|
110
|
-
if (
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
138
|
+
if (has_schema_file) {
|
|
139
|
+
const { schema_file_path } = await p.group(
|
|
140
|
+
{
|
|
141
|
+
schema_file_path: () => p.path({
|
|
142
|
+
message: "Where is the schema file?",
|
|
143
|
+
initialValue: "./schema.graphql",
|
|
144
|
+
validate: (value) => {
|
|
145
|
+
if (!value) return "Please enter a valid path";
|
|
146
|
+
try {
|
|
147
|
+
fs.statSync(path.resolve(value));
|
|
148
|
+
} catch {
|
|
149
|
+
return "File not found";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
},
|
|
154
|
+
{ onCancel: () => pCancel() }
|
|
155
|
+
);
|
|
156
|
+
pullSchema_content = await fs.readFile(path.resolve(schema_file_path)) ?? null;
|
|
115
157
|
}
|
|
116
|
-
url =
|
|
117
|
-
}
|
|
118
|
-
if (pullSchema_content === null) {
|
|
119
|
-
pCancel("We couldn't pull the schema. Please check your URL/headers and try again.");
|
|
158
|
+
url = "API_URL";
|
|
120
159
|
}
|
|
121
160
|
} else if (!args.yes) {
|
|
122
161
|
const answers = await p.group(
|
|
123
162
|
{
|
|
124
|
-
schema_path: () => p.
|
|
163
|
+
schema_path: () => p.path({
|
|
125
164
|
message: "Where is your schema located?",
|
|
126
|
-
|
|
165
|
+
initialValue: schemaPath,
|
|
127
166
|
validate: (value) => {
|
|
128
|
-
if (value
|
|
129
|
-
return "Please enter a valid schemaPath";
|
|
130
|
-
}
|
|
167
|
+
if (!value) return "Please enter a valid schemaPath";
|
|
131
168
|
}
|
|
132
169
|
})
|
|
133
170
|
},
|
|
@@ -173,17 +210,13 @@ async function init(_path, args) {
|
|
|
173
210
|
is_remote_endpoint ? url : null,
|
|
174
211
|
runtimeDir
|
|
175
212
|
);
|
|
176
|
-
await houdiniClient(sourceDir, typescript, frameworkInfo, url);
|
|
213
|
+
await houdiniClient(sourceDir, typescript, frameworkInfo, is_remote_endpoint ? url : null);
|
|
177
214
|
if (frameworkInfo.framework === "svelte") {
|
|
178
215
|
await svelteKitConfig(targetPath, typescript);
|
|
179
216
|
} else if (frameworkInfo.framework === "kit") {
|
|
180
217
|
await svelteConfig(targetPath, typescript);
|
|
181
218
|
}
|
|
182
|
-
await gitIgnore({
|
|
183
|
-
targetPath,
|
|
184
|
-
runtimeDir,
|
|
185
|
-
schemaPath: is_remote_endpoint ? schemaPath : void 0
|
|
186
|
-
});
|
|
219
|
+
await gitIgnore({ targetPath, runtimeDir });
|
|
187
220
|
await graphqlRC(targetPath, runtimeDir);
|
|
188
221
|
await viteConfig(targetPath, frameworkInfo, typescript);
|
|
189
222
|
await tjsConfig(targetPath, frameworkInfo);
|
|
@@ -225,7 +258,7 @@ async function houdiniConfig(configPath, schemaPath, module, frameworkInfo, url,
|
|
|
225
258
|
};
|
|
226
259
|
}
|
|
227
260
|
config.runtimeDir = runtimeDir;
|
|
228
|
-
if (
|
|
261
|
+
if (url !== null) {
|
|
229
262
|
config.schemaPath = schemaPath;
|
|
230
263
|
}
|
|
231
264
|
if (module !== "esm") {
|
|
@@ -247,24 +280,26 @@ async function houdiniConfig(configPath, schemaPath, module, frameworkInfo, url,
|
|
|
247
280
|
|
|
248
281
|
/** @type {import('houdini').ConfigFile} */
|
|
249
282
|
const config = ${configObj}`;
|
|
250
|
-
const content = module === "esm" ?
|
|
283
|
+
const content = module === "esm" ? (
|
|
284
|
+
// ESM default config
|
|
285
|
+
`${content_base}
|
|
251
286
|
|
|
252
287
|
export default config
|
|
253
|
-
`
|
|
288
|
+
`
|
|
289
|
+
) : (
|
|
290
|
+
// CommonJS default config
|
|
291
|
+
`${content_base}
|
|
254
292
|
|
|
255
293
|
module.exports = config
|
|
256
|
-
|
|
294
|
+
`
|
|
295
|
+
);
|
|
257
296
|
await fs.writeFile(configPath, content);
|
|
258
297
|
return false;
|
|
259
298
|
}
|
|
260
299
|
async function houdiniClient(targetPath, typescript, _frameworkInfo, url) {
|
|
261
300
|
const houdiniClientExt = typescript ? `ts` : `js`;
|
|
262
301
|
const houdiniClientPath = path.join(targetPath, `client.${houdiniClientExt}`);
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
export default new HoudiniClient({
|
|
266
|
-
url: '${url}'
|
|
267
|
-
|
|
302
|
+
const comment = `
|
|
268
303
|
// uncomment this to configure the network call (for things like authentication)
|
|
269
304
|
// for more information, please visit here: https://www.houdinigraphql.com/guides/authentication
|
|
270
305
|
// fetchParams({ session }) {
|
|
@@ -273,8 +308,14 @@ export default new HoudiniClient({
|
|
|
273
308
|
// Authorization: \`Bearer \${session.token}\`,
|
|
274
309
|
// }
|
|
275
310
|
// }
|
|
276
|
-
// }
|
|
277
|
-
|
|
311
|
+
// }`;
|
|
312
|
+
const urlLine = url ? `{
|
|
313
|
+
url: '${url}',${comment}
|
|
314
|
+
}` : `{${comment}
|
|
315
|
+
}`;
|
|
316
|
+
const content = `import { HoudiniClient } from '$houdini';
|
|
317
|
+
|
|
318
|
+
export default new HoudiniClient(${urlLine})
|
|
278
319
|
`;
|
|
279
320
|
await fs.writeFile(houdiniClientPath, content);
|
|
280
321
|
}
|
|
@@ -329,29 +370,15 @@ export default config;
|
|
|
329
370
|
`;
|
|
330
371
|
await fs.writeFile(svelteConfigPath, typescript ? newContentTs : newContentJs);
|
|
331
372
|
}
|
|
332
|
-
async function gitIgnore({
|
|
333
|
-
targetPath,
|
|
334
|
-
runtimeDir,
|
|
335
|
-
schemaPath
|
|
336
|
-
}) {
|
|
373
|
+
async function gitIgnore({ targetPath, runtimeDir }) {
|
|
337
374
|
const filepath = path.join(targetPath, ".gitignore");
|
|
338
375
|
const existing = await fs.readFile(filepath) || "";
|
|
339
|
-
let newIgnores = "";
|
|
340
376
|
if (!existing.includes(`
|
|
341
377
|
${runtimeDir}
|
|
342
378
|
`)) {
|
|
343
|
-
newIgnores += `${runtimeDir}
|
|
344
|
-
`;
|
|
345
|
-
}
|
|
346
|
-
if (schemaPath && !existing.includes(`
|
|
347
|
-
${schemaPath}
|
|
348
|
-
`)) {
|
|
349
|
-
newIgnores += `${schemaPath}
|
|
350
|
-
`;
|
|
351
|
-
}
|
|
352
|
-
if (newIgnores) {
|
|
353
379
|
await fs.writeFile(filepath, `${existing}
|
|
354
|
-
${
|
|
380
|
+
${runtimeDir}
|
|
381
|
+
`);
|
|
355
382
|
}
|
|
356
383
|
}
|
|
357
384
|
async function graphqlRC(targetPath, runtimeDir) {
|
|
@@ -445,12 +472,12 @@ async function packageJSON(targetPath, frameworkInfo) {
|
|
|
445
472
|
}
|
|
446
473
|
packageJSON2.devDependencies = {
|
|
447
474
|
...packageJSON2.devDependencies,
|
|
448
|
-
houdini: "^2.0.0-next.
|
|
475
|
+
houdini: "^2.0.0-next.29"
|
|
449
476
|
};
|
|
450
477
|
if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
|
|
451
478
|
packageJSON2.devDependencies = {
|
|
452
479
|
...packageJSON2.devDependencies,
|
|
453
|
-
"houdini-svelte": "^3.0.0-next.
|
|
480
|
+
"houdini-svelte": "^3.0.0-next.30"
|
|
454
481
|
};
|
|
455
482
|
} else {
|
|
456
483
|
throw new Error(`Unmanaged framework: "${JSON.stringify(frameworkInfo)}"`);
|
package/build/lib/ast.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export declare function find_exported_fn(body: Statement[], name: string): {
|
|
|
12
12
|
declaration: FunctionDeclaration | FunctionExpression | ArrowFunctionExpression | null | Identifier | CallExpression;
|
|
13
13
|
export: ExportNamedDeclaration;
|
|
14
14
|
} | null;
|
|
15
|
-
export declare function find_exported_id(program: Program, name: string): recast.types.namedTypes.ExportNamedDeclaration;
|
|
15
|
+
export declare function find_exported_id(program: Program, name: string): recast.types.namedTypes.ExportNamedDeclaration | undefined;
|
|
16
16
|
export {};
|
package/build/lib/codegen.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type DatabaseSync } from 'node:sqlite';
|
|
2
1
|
import * as conventions from '../router/conventions.js';
|
|
3
2
|
import type { Config } from './config.js';
|
|
3
|
+
import { type Db } from './db.js';
|
|
4
4
|
import type { ProjectManifest } from './types.js';
|
|
5
5
|
export type PluginSpec = {
|
|
6
6
|
name: string;
|
|
@@ -30,8 +30,8 @@ export type Adapter = ((args: {
|
|
|
30
30
|
outDir: string;
|
|
31
31
|
}) => Promise<void> | void;
|
|
32
32
|
};
|
|
33
|
-
export declare function connect_db(config: Config): [
|
|
34
|
-
export declare function init_db(config: Config, preserve: boolean): Promise<[
|
|
33
|
+
export declare function connect_db(config: Config): Promise<[Db, string]>;
|
|
34
|
+
export declare function init_db(config: Config, preserve: boolean): Promise<[Db, string]>;
|
|
35
35
|
export type CompilerProxy = {
|
|
36
36
|
close: () => Promise<void>;
|
|
37
37
|
trigger_hook: (name: PipelineHook, opts?: {
|
|
@@ -43,7 +43,7 @@ export type CompilerProxy = {
|
|
|
43
43
|
run_pipeline: (options: RunPipelineOptions) => Promise<Record<PipelineHook, Record<string, any>>>;
|
|
44
44
|
};
|
|
45
45
|
export declare function plugin_db_key(name: string): string;
|
|
46
|
-
export declare function codegen_setup(config: Config, mode: string, db:
|
|
46
|
+
export declare function codegen_setup(config: Config, mode: string, db: Db, db_file: string): Promise<CompilerProxy>;
|
|
47
47
|
export declare const PIPELINE_HOOKS: readonly ["Config", "AfterLoad", "Schema", "ExtractDocuments", "AfterExtract", "BeforeValidate", "Validate", "AfterValidate", "BeforeGenerate", "GenerateDocuments", "GenerateRuntime", "AfterGenerate"];
|
|
48
48
|
export type PipelineHook = (typeof PIPELINE_HOOKS)[number];
|
|
49
49
|
export type RunPipelineOptions = {
|
package/build/lib/codegen.js
CHANGED
|
@@ -1,25 +1,66 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
2
4
|
import path from "node:path";
|
|
3
5
|
import { createInterface } from "node:readline";
|
|
4
|
-
import sqlite from "node:sqlite";
|
|
5
6
|
import { WebSocket } from "ws";
|
|
7
|
+
const wasiRunnerPath = path.join(tmpdir(), "houdini-wasi-runner.mjs");
|
|
8
|
+
writeFileSync(
|
|
9
|
+
wasiRunnerPath,
|
|
10
|
+
`import{WASI}from'node:wasi';
|
|
11
|
+
import{readFileSync}from'node:fs';
|
|
12
|
+
import{Worker,isMainThread,workerData,MessageChannel,receiveMessageOnPort}from'worker_threads';
|
|
13
|
+
const[,,w,...r]=process.argv;
|
|
14
|
+
if(isMainThread){
|
|
15
|
+
const{port1:p1,port2:p2}=new MessageChannel();
|
|
16
|
+
const sb=new Int32Array(new SharedArrayBuffer(4));
|
|
17
|
+
process.stdin.on('error',()=>{});
|
|
18
|
+
process.stdin.on('data',d=>{p1.postMessage(d);Atomics.add(sb,0,1);Atomics.notify(sb,0);});
|
|
19
|
+
process.stdin.on('end',()=>{p1.postMessage(null);Atomics.add(sb,0,1);Atomics.notify(sb,0);});
|
|
20
|
+
const wk=new Worker(new URL(import.meta.url),{workerData:{w,r,p:p2,sb},transferList:[p2]});
|
|
21
|
+
wk.on('exit',c=>process.exit(c??0));
|
|
22
|
+
}else{
|
|
23
|
+
const{w:wb,r:args,p:port,sb}=workerData;
|
|
24
|
+
const wasi=new WASI({version:'preview1',args:[wb,...args],env:process.env,preopens:{'/':'/'}});
|
|
25
|
+
let mem=null;
|
|
26
|
+
const io=wasi.getImportObject();
|
|
27
|
+
const rfr=io.wasi_snapshot_preview1.fd_read;
|
|
28
|
+
io.wasi_snapshot_preview1.fd_read=(fd,iovs,il,nr)=>{
|
|
29
|
+
if(fd!==0||!mem)return rfr(fd,iovs,il,nr);
|
|
30
|
+
while(Atomics.load(sb,0)===0)Atomics.wait(sb,0,0);
|
|
31
|
+
const m=receiveMessageOnPort(port);
|
|
32
|
+
Atomics.sub(sb,0,1);
|
|
33
|
+
const v=new DataView(mem.buffer);
|
|
34
|
+
if(!m||m.message===null){v.setUint32(nr,0,true);return 0;}
|
|
35
|
+
const b=Buffer.isBuffer(m.message)?m.message:Buffer.from(m.message);
|
|
36
|
+
let wn=0;
|
|
37
|
+
for(let i=0;i<il;i++){
|
|
38
|
+
const p=v.getUint32(iovs+i*8,true),l=v.getUint32(iovs+i*8+4,true);
|
|
39
|
+
const n=Math.min(l,b.length-wn);
|
|
40
|
+
if(n<=0)break;
|
|
41
|
+
new Uint8Array(mem.buffer,p,n).set(b.subarray(wn,wn+n));
|
|
42
|
+
wn+=n;}
|
|
43
|
+
v.setUint32(nr,wn,true);
|
|
44
|
+
return 0;};
|
|
45
|
+
const mod=new WebAssembly.Module(readFileSync(wb));
|
|
46
|
+
const inst=new WebAssembly.Instance(mod,io);
|
|
47
|
+
mem=inst.exports.memory;
|
|
48
|
+
wasi.start(inst);
|
|
49
|
+
process.exit(0);}
|
|
50
|
+
`
|
|
51
|
+
);
|
|
6
52
|
import * as conventions from "../router/conventions.js";
|
|
53
|
+
import { openDb } from "./db.js";
|
|
7
54
|
import { create_schema, write_config } from "./database.js";
|
|
8
55
|
import { format_hook_error } from "./error.js";
|
|
9
56
|
import * as fs from "./fs.js";
|
|
10
57
|
import { Logger } from "./logger.js";
|
|
11
58
|
import { LogLevel } from "./types.js";
|
|
12
|
-
function connect_db(config) {
|
|
59
|
+
async function connect_db(config) {
|
|
13
60
|
const filepath = conventions.db_path(config);
|
|
14
|
-
const db =
|
|
15
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
16
|
-
db.exec("PRAGMA synchronous = off");
|
|
17
|
-
db.exec("PRAGMA cache_size = 10000");
|
|
18
|
-
db.exec("PRAGMA temp_store = memory");
|
|
19
|
-
db.exec("PRAGMA busy_timeout = 5000");
|
|
20
|
-
db.exec("PRAGMA foreign_key = ON");
|
|
21
|
-
db.exec("PRAGMA defer_foreign_keys = ON");
|
|
61
|
+
const db = await openDb(filepath);
|
|
22
62
|
db.exec(create_schema);
|
|
63
|
+
db.flush();
|
|
23
64
|
return [db, filepath];
|
|
24
65
|
}
|
|
25
66
|
async function init_db(config, preserve) {
|
|
@@ -38,7 +79,7 @@ async function init_db(config, preserve) {
|
|
|
38
79
|
} catch (_e) {
|
|
39
80
|
}
|
|
40
81
|
}
|
|
41
|
-
return
|
|
82
|
+
return connect_db(config);
|
|
42
83
|
}
|
|
43
84
|
function plugin_db_key(name) {
|
|
44
85
|
if (!name.startsWith("./") && !name.startsWith("../") && !path.isAbsolute(name)) {
|
|
@@ -47,6 +88,7 @@ function plugin_db_key(name) {
|
|
|
47
88
|
return name.replace(/\./g, "_").replace(/\//g, "__");
|
|
48
89
|
}
|
|
49
90
|
async function codegen_setup(config, mode, db, db_file) {
|
|
91
|
+
let _db = db;
|
|
50
92
|
const logger = new Logger(config.config_file.logLevel ?? LogLevel.Summary);
|
|
51
93
|
await fs.mkdirpSync(conventions.houdini_root(config));
|
|
52
94
|
const rawTransport = config.config_file.pluginTransport ?? "websocket";
|
|
@@ -57,47 +99,51 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
57
99
|
const spec_results = {};
|
|
58
100
|
const stdioStdin = /* @__PURE__ */ new Map();
|
|
59
101
|
const triggerHookRef = { fn: null };
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
102
|
+
const pendingRequests = /* @__PURE__ */ new Map();
|
|
103
|
+
const wait_for_plugin_db = async (configKey, dbKey) => {
|
|
104
|
+
const pollDb = await openDb(db_file);
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
const interval = setInterval(() => {
|
|
107
|
+
pollDb.reload();
|
|
108
|
+
const row = pollDb.get("SELECT * FROM plugins WHERE name = ?", [dbKey]);
|
|
109
|
+
if (row) {
|
|
110
|
+
clearInterval(interval);
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
pollDb.run("UPDATE plugins SET config = ? WHERE name = ?", [
|
|
113
|
+
JSON.stringify(
|
|
114
|
+
config.plugins.find((p) => p.name === configKey)?.config ?? {}
|
|
115
|
+
),
|
|
116
|
+
dbKey
|
|
117
|
+
]);
|
|
118
|
+
pollDb.flush();
|
|
119
|
+
pollDb.close();
|
|
120
|
+
const spec = {
|
|
121
|
+
name: row.name,
|
|
122
|
+
port: row.port,
|
|
123
|
+
hooks: new Set(JSON.parse(row.hooks)),
|
|
124
|
+
order: row.plugin_order,
|
|
125
|
+
directory: config.plugins.find((p) => p.name === configKey)?.directory || ""
|
|
126
|
+
};
|
|
127
|
+
spec_results[configKey] = spec;
|
|
128
|
+
if (row.config_module) {
|
|
129
|
+
import(row.config_module).then((module) => {
|
|
130
|
+
if (module && typeof module.default === "function") {
|
|
131
|
+
config.config_file = module.default(config.config_file);
|
|
132
|
+
}
|
|
133
|
+
resolve(spec);
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
resolve(spec);
|
|
137
|
+
}
|
|
89
138
|
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
resolve(spec);
|
|
99
|
-
};
|
|
100
|
-
});
|
|
139
|
+
}, 10);
|
|
140
|
+
const timeout = setTimeout(() => {
|
|
141
|
+
clearInterval(interval);
|
|
142
|
+
pollDb.close();
|
|
143
|
+
reject(new Error(`Timeout waiting for plugin ${configKey} to register`));
|
|
144
|
+
}, 1e4);
|
|
145
|
+
});
|
|
146
|
+
};
|
|
101
147
|
const wait_for_plugin_stdio = (name, child) => new Promise((resolve, reject) => {
|
|
102
148
|
const timeout = setTimeout(() => {
|
|
103
149
|
reject(new Error(`Timeout waiting for plugin ${name} to register`));
|
|
@@ -123,24 +169,26 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
123
169
|
directory: config.plugins.find((p) => p.name === name)?.directory || ""
|
|
124
170
|
};
|
|
125
171
|
spec_results[name] = spec;
|
|
126
|
-
|
|
172
|
+
_db.run(
|
|
127
173
|
`INSERT OR IGNORE INTO plugins (name, hooks, port, plugin_order, include_runtime, config_module, client_plugins)
|
|
128
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
174
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
175
|
+
[
|
|
176
|
+
spec.name,
|
|
177
|
+
JSON.stringify([...spec.hooks]),
|
|
178
|
+
spec.port,
|
|
179
|
+
spec.order,
|
|
180
|
+
msg.includeRuntime ?? null,
|
|
181
|
+
msg.configModule ?? null,
|
|
182
|
+
msg.clientPlugins ?? null
|
|
183
|
+
]
|
|
137
184
|
);
|
|
138
|
-
|
|
185
|
+
_db.run("UPDATE plugins SET config = ? WHERE name = ?", [
|
|
139
186
|
JSON.stringify(
|
|
140
187
|
config.plugins.find((p) => p.name === name)?.config ?? {}
|
|
141
188
|
),
|
|
142
189
|
spec.name
|
|
143
|
-
);
|
|
190
|
+
]);
|
|
191
|
+
_db.flush();
|
|
144
192
|
if (msg.configModule) {
|
|
145
193
|
import(msg.configModule).then((module) => {
|
|
146
194
|
if (module && typeof module.default === "function") {
|
|
@@ -161,8 +209,7 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
161
209
|
}
|
|
162
210
|
if (msg.type === "response") {
|
|
163
211
|
const pending = pendingRequests.get(msg.id);
|
|
164
|
-
if (!pending)
|
|
165
|
-
return;
|
|
212
|
+
if (!pending) return;
|
|
166
213
|
clearTimeout(pending.timeout);
|
|
167
214
|
pendingRequests.delete(msg.id);
|
|
168
215
|
if (msg.error) {
|
|
@@ -214,8 +261,7 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
214
261
|
reject(new Error(`Plugin ${name} stdout closed before registering`));
|
|
215
262
|
} else {
|
|
216
263
|
for (const [id, pending] of pendingRequests.entries()) {
|
|
217
|
-
if (pending.plugin !== name)
|
|
218
|
-
continue;
|
|
264
|
+
if (pending.plugin !== name) continue;
|
|
219
265
|
clearTimeout(pending.timeout);
|
|
220
266
|
pendingRequests.delete(id);
|
|
221
267
|
pending.reject(new Error(`Plugin ${name} closed unexpectedly`));
|
|
@@ -223,7 +269,8 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
223
269
|
}
|
|
224
270
|
});
|
|
225
271
|
});
|
|
226
|
-
|
|
272
|
+
_db.run("DELETE FROM plugins");
|
|
273
|
+
_db.flush();
|
|
227
274
|
logger.time("Start Plugins");
|
|
228
275
|
await Promise.all(
|
|
229
276
|
config.plugins.map(async (plugin) => {
|
|
@@ -233,18 +280,24 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
233
280
|
if (jsExtensions.includes(path.extname(plugin.executable))) {
|
|
234
281
|
executable = "node";
|
|
235
282
|
args.unshift(plugin.executable);
|
|
283
|
+
} else if (path.extname(plugin.executable) === ".wasm") {
|
|
284
|
+
executable = "node";
|
|
285
|
+
args.unshift(wasiRunnerPath, plugin.executable);
|
|
236
286
|
}
|
|
237
287
|
const dbKey = plugin_db_key(plugin.name);
|
|
238
288
|
args.push("--plugin-key", dbKey);
|
|
239
|
-
|
|
289
|
+
const pluginUsesStdio = useStdio || path.extname(plugin.executable) === ".wasm";
|
|
290
|
+
if (pluginUsesStdio) {
|
|
240
291
|
args.push("--transport", "stdio");
|
|
241
292
|
}
|
|
242
293
|
logger.time(`Spawn ${plugin.name}`);
|
|
243
294
|
const child = spawn(executable, args, {
|
|
244
|
-
|
|
295
|
+
// [stdin, stdout, stderr]: stdio plugins need piped stdin/stdout for the
|
|
296
|
+
// message protocol; stderr is always inherited so plugin logs reach the terminal.
|
|
297
|
+
stdio: pluginUsesStdio ? ["pipe", "pipe", "inherit"] : ["inherit", "inherit", "inherit"],
|
|
245
298
|
detached: process.platform !== "win32"
|
|
246
299
|
});
|
|
247
|
-
if (
|
|
300
|
+
if (pluginUsesStdio) {
|
|
248
301
|
stdioStdin.set(dbKey, child.stdin);
|
|
249
302
|
child.stdin.on("error", (err) => {
|
|
250
303
|
if (err.code !== "EPIPE") {
|
|
@@ -254,7 +307,7 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
254
307
|
}
|
|
255
308
|
plugins[plugin.name] = {
|
|
256
309
|
process: child,
|
|
257
|
-
...await (
|
|
310
|
+
...await (pluginUsesStdio ? wait_for_plugin_stdio(plugin.name, child) : wait_for_plugin_db(plugin.name, dbKey))
|
|
258
311
|
};
|
|
259
312
|
logger.timeEnd(`Spawn ${plugin.name}`, LogLevel.Verbose);
|
|
260
313
|
})
|
|
@@ -265,7 +318,6 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
265
318
|
logger.timeEnd("Start Plugins", LogLevel.Summary);
|
|
266
319
|
const wsConnections = /* @__PURE__ */ new Map();
|
|
267
320
|
let messageCounter = 0;
|
|
268
|
-
const pendingRequests = /* @__PURE__ */ new Map();
|
|
269
321
|
async function getOrCreateWS(name, port) {
|
|
270
322
|
const existing = wsConnections.get(name);
|
|
271
323
|
if (existing && existing.readyState === WebSocket.OPEN) {
|
|
@@ -282,8 +334,7 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
282
334
|
try {
|
|
283
335
|
const response = JSON.parse(data.toString());
|
|
284
336
|
const pending = pendingRequests.get(response.id);
|
|
285
|
-
if (!pending)
|
|
286
|
-
return;
|
|
337
|
+
if (!pending) return;
|
|
287
338
|
clearTimeout(pending.timeout);
|
|
288
339
|
pendingRequests.delete(response.id);
|
|
289
340
|
switch (response.type) {
|
|
@@ -361,7 +412,8 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
361
412
|
});
|
|
362
413
|
}
|
|
363
414
|
};
|
|
364
|
-
|
|
415
|
+
_db.reload();
|
|
416
|
+
const _fireHook = async (hook, {
|
|
365
417
|
parallel_safe,
|
|
366
418
|
payload,
|
|
367
419
|
task_id
|
|
@@ -387,8 +439,15 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
387
439
|
}
|
|
388
440
|
return result;
|
|
389
441
|
};
|
|
442
|
+
const trigger_hook = async (hook, opts) => {
|
|
443
|
+
_db.flush();
|
|
444
|
+
const result = await _fireHook(hook, opts);
|
|
445
|
+
_db.reload();
|
|
446
|
+
return result;
|
|
447
|
+
};
|
|
390
448
|
triggerHookRef.fn = trigger_hook;
|
|
391
|
-
await write_config(
|
|
449
|
+
await write_config(_db, config, invoke_hook, plugin_specs, mode, logger);
|
|
450
|
+
_db.flush();
|
|
392
451
|
await trigger_hook("Config");
|
|
393
452
|
await trigger_hook("AfterLoad");
|
|
394
453
|
await trigger_hook("Schema");
|
package/build/lib/config.d.ts
CHANGED
|
@@ -214,7 +214,7 @@ export declare class Config {
|
|
|
214
214
|
});
|
|
215
215
|
schema_path(): string;
|
|
216
216
|
get localApiDir(): string;
|
|
217
|
-
api_url(): Promise<string>;
|
|
217
|
+
api_url(): Promise<string | undefined>;
|
|
218
218
|
get include(): Array<string>;
|
|
219
219
|
includeFile(filepath: string, { root }?: {
|
|
220
220
|
root?: string;
|
|
@@ -225,7 +225,7 @@ export declare class Config {
|
|
|
225
225
|
root?: string;
|
|
226
226
|
}): boolean;
|
|
227
227
|
schema_pull_headers(): Promise<any>;
|
|
228
|
-
process_env_values(env: Record<string, string | undefined>, value: string | ((env: any) => string)): string;
|
|
228
|
+
process_env_values(env: Record<string, string | undefined>, value: string | ((env: any) => string)): string | undefined;
|
|
229
229
|
get artifact_dir(): string;
|
|
230
230
|
get routes_dir(): string;
|
|
231
231
|
artifactPath(document: graphql.DocumentNode): string;
|