houdini 2.0.0-next.31 → 2.0.0-next.32
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 +1 -1
- package/build/lib/codegen.d.ts +2 -0
- package/build/lib/codegen.js +17 -5
- package/build/lib/database.d.ts +1 -1
- package/build/lib/database.js +1 -0
- package/build/lib/plugins.js +39 -35
- package/build/node/index.d.ts +6 -2
- package/build/node/index.js +170 -36
- package/build/package.json +1 -1
- package/build/runtime/cache/index.js +1 -3
- package/build/vite/hmr.d.ts +3 -3
- package/build/vite/hmr.js +295 -163
- package/build/vite/houdini.js +6 -0
- package/build/vite/schema.js +8 -4
- package/package.json +2 -2
package/build/cmd/init.js
CHANGED
|
@@ -472,7 +472,7 @@ async function packageJSON(targetPath, frameworkInfo) {
|
|
|
472
472
|
}
|
|
473
473
|
packageJSON2.devDependencies = {
|
|
474
474
|
...packageJSON2.devDependencies,
|
|
475
|
-
houdini: "^2.0.0-next.
|
|
475
|
+
houdini: "^2.0.0-next.32"
|
|
476
476
|
};
|
|
477
477
|
if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
|
|
478
478
|
packageJSON2.devDependencies = {
|
package/build/lib/codegen.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export type CompilerProxy = {
|
|
|
41
41
|
}) => Promise<Record<string, any> | null>;
|
|
42
42
|
database_path: string;
|
|
43
43
|
run_pipeline: (options: RunPipelineOptions) => Promise<Record<PipelineHook, Record<string, any>>>;
|
|
44
|
+
/** Serializes concurrent pipeline runs (HMR vs schema watcher) via a promise chain. */
|
|
45
|
+
pipeline_lock: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
44
46
|
};
|
|
45
47
|
export declare function plugin_db_key(name: string): string;
|
|
46
48
|
export declare function codegen_setup(config: Config, mode: string, db: Db, db_file: string): Promise<CompilerProxy>;
|
package/build/lib/codegen.js
CHANGED
|
@@ -50,8 +50,8 @@ process.exit(0);}
|
|
|
50
50
|
`
|
|
51
51
|
);
|
|
52
52
|
import * as conventions from "../router/conventions.js";
|
|
53
|
-
import { openDb } from "./db.js";
|
|
54
53
|
import { create_schema, write_config } from "./database.js";
|
|
54
|
+
import { openDb } from "./db.js";
|
|
55
55
|
import { format_hook_error } from "./error.js";
|
|
56
56
|
import * as fs from "./fs.js";
|
|
57
57
|
import { Logger } from "./logger.js";
|
|
@@ -88,8 +88,8 @@ function plugin_db_key(name) {
|
|
|
88
88
|
return name.replace(/\./g, "_").replace(/\//g, "__");
|
|
89
89
|
}
|
|
90
90
|
async function codegen_setup(config, mode, db, db_file) {
|
|
91
|
-
|
|
92
|
-
const logger = new Logger(config.config_file.logLevel ?? LogLevel.
|
|
91
|
+
const _db = db;
|
|
92
|
+
const logger = new Logger(config.config_file.logLevel ?? LogLevel.ShortSummary);
|
|
93
93
|
await fs.mkdirpSync(conventions.houdini_root(config));
|
|
94
94
|
const rawTransport = config.config_file.pluginTransport ?? "websocket";
|
|
95
95
|
const resolvedTransport = rawTransport.startsWith("env:") ? process.env[rawTransport.slice("env:".length)] ?? "websocket" : rawTransport;
|
|
@@ -170,14 +170,15 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
170
170
|
};
|
|
171
171
|
spec_results[name] = spec;
|
|
172
172
|
_db.run(
|
|
173
|
-
`INSERT OR IGNORE INTO plugins (name, hooks, port, plugin_order, include_runtime, config_module, client_plugins)
|
|
174
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
173
|
+
`INSERT OR IGNORE INTO plugins (name, hooks, port, plugin_order, include_runtime, include_static_runtime, config_module, client_plugins)
|
|
174
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
175
175
|
[
|
|
176
176
|
spec.name,
|
|
177
177
|
JSON.stringify([...spec.hooks]),
|
|
178
178
|
spec.port,
|
|
179
179
|
spec.order,
|
|
180
180
|
msg.includeRuntime ?? null,
|
|
181
|
+
msg.includeStaticRuntime ?? null,
|
|
181
182
|
msg.configModule ?? null,
|
|
182
183
|
msg.clientPlugins ?? null
|
|
183
184
|
]
|
|
@@ -451,10 +452,21 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
451
452
|
await trigger_hook("Config");
|
|
452
453
|
await trigger_hook("AfterLoad");
|
|
453
454
|
await trigger_hook("Schema");
|
|
455
|
+
let pipelineQueue = Promise.resolve();
|
|
454
456
|
return {
|
|
455
457
|
database_path: db_file,
|
|
456
458
|
trigger_hook,
|
|
457
459
|
run_pipeline: (options) => run_pipeline(trigger_hook, options),
|
|
460
|
+
pipeline_lock: (fn) => {
|
|
461
|
+
const result = pipelineQueue.then(fn);
|
|
462
|
+
pipelineQueue = result.then(
|
|
463
|
+
() => {
|
|
464
|
+
},
|
|
465
|
+
() => {
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
return result;
|
|
469
|
+
},
|
|
458
470
|
close: async () => {
|
|
459
471
|
for (const [name, ws] of wsConnections.entries()) {
|
|
460
472
|
try {
|
package/build/lib/database.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import type { PluginSpec } from './codegen.js';
|
|
|
2
2
|
import type { Db } from './db.js';
|
|
3
3
|
import type { Config } from './config.js';
|
|
4
4
|
import { Logger } from './logger.js';
|
|
5
|
-
export declare const create_schema = "\nCREATE TABLE IF NOT EXISTS plugins (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n port INTEGER NOT NULL,\n hooks JSON NOT NULL,\n plugin_order TEXT CHECK (plugin_order IS NULL OR plugin_order IN ('before', 'after', 'core')),\n include_runtime TEXT,\n include_static_runtime TEXT,\n config JSON,\n\t config_module TEXT,\n\t\tclient_plugins JSON\n);\n\n-- Watch Schema Config\nCREATE TABLE IF NOT EXISTS watch_schema_config (\n url TEXT NOT NULL,\n headers JSON,\n interval INTEGER,\n timeout INTEGER\n);\n\n-- Router Config\nCREATE TABLE IF NOT EXISTS router_config (\n api_endpoint TEXT,\n redirect TEXT UNIQUE,\n session_keys TEXT NOT NULL UNIQUE,\n url TEXT,\n mutation TEXT UNIQUE\n);\n\n-- Runtime Scalar Definition\nCREATE TABLE IF NOT EXISTS runtime_scalar_definitions (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n type TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS component_fields (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tdocument INTEGER NOT NULL,\n type TEXT,\n\tprop TEXT,\n field TEXT,\n\tinline BOOLEAN default false,\n type_field TEXT,\n fragment TEXT,\n\tUNIQUE (document),\n\tFOREIGN KEY (document) REFERENCES raw_documents(id) ON DELETE CASCADE\n);\n\n-- Static Config (main config table)\nCREATE TABLE IF NOT EXISTS config (\n include JSON NOT NULL,\n exclude JSON NOT NULL,\n schema_path TEXT NOT NULL,\n definitions_path TEXT,\n cache_buffer_size INTEGER,\n default_cache_policy TEXT,\n default_partial BOOLEAN,\n default_lifetime INTEGER,\n default_list_position TEXT CHECK (default_list_position IN ('APPEND', 'PREPEND')),\n default_list_target TEXT CHECK (default_list_target IN ('ALL', 'NULL')),\n default_paginate_mode TEXT CHECK (default_paginate_mode IN ('Infinite', 'SinglePage')),\n suppress_pagination_deduplication BOOLEAN,\n log_level TEXT CHECK (log_level IN ('QUIET', 'FULL', 'SUMMARY', 'SHORT_SUMMARY')),\n default_fragment_masking BOOLEAN,\n default_keys JSON,\n persisted_queries_path TEXT NOT NULL,\n project_root TEXT,\n runtime_dir TEXT,\n\t\tpath TEXT\n);\n\nCREATE TABLE IF NOT EXISTS scalar_config (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n type TEXT NOT NULL,\n\tinput_types JSON,\n\tmodule TEXT,\n\tdefault_import BOOLEAN\n);\n\n-- Types configuration\nCREATE TABLE IF NOT EXISTS type_configs (\n name TEXT NOT NULL,\n keys JSON NOT NULL,\n\tresolve_query TEXT\n);\n\n-- A table of original document contents (to be populated by plugins)\nCREATE TABLE IF NOT EXISTS raw_documents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n offset_line INTEGER,\n offset_column INTEGER,\n filepath TEXT NOT NULL,\n content TEXT NOT NULL,\n current_task TEXT,\n loaded_with TEXT\n);\n\n-----------------------------------------------------------\n-- Schema Definition Tables\n-----------------------------------------------------------\n\nCREATE TABLE IF NOT EXISTS types (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n kind TEXT NOT NULL CHECK (kind IN ('OBJECT', 'INTERFACE', 'UNION', 'ENUM', 'SCALAR', 'INPUT')),\n operation TEXT,\n\tdescription TEXT,\n\tinternal BOOLEAN default false,\n\tbuilt_in BOOLEAN default false\n);\n\nCREATE TABLE IF NOT EXISTS type_fields (\n id TEXT PRIMARY KEY, -- will be something like User.name so we don't have to look up the generated id\n parent TEXT NOT NULL, -- will be User\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n\t type_modifiers TEXT,\n default_value TEXT,\n description TEXT,\n\t internal BOOLEAN default false,\n document INT,\n\n FOREIGN KEY (document) REFERENCES raw_documents(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (type) REFERENCES types(name) ON DELETE CASCADE,\n UNIQUE (parent, name)\n);\n\nCREATE TABLE IF NOT EXISTS type_field_arguments (\n id TEXT PRIMARY KEY,\n field TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n type_modifiers TEXT,\n default_value TEXT,\n FOREIGN KEY (field) REFERENCES type_fields(id) ON DELETE CASCADE,\n UNIQUE (field, name)\n);\n\n\nCREATE TABLE IF NOT EXISTS enum_values (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent TEXT NOT NULL,\n value TEXT NOT NULL,\n description TEXT,\n FOREIGN KEY (parent) REFERENCES types(name) ON DELETE CASCADE,\n UNIQUE (parent, value)\n);\n\nCREATE TABLE IF NOT EXISTS possible_types (\n type TEXT NOT NULL,\n member TEXT NOT NULL,\n FOREIGN KEY (type) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (member) REFERENCES types(name) ON DELETE CASCADE,\n PRIMARY KEY (type, member)\n);\n\nCREATE TABLE IF NOT EXISTS directives (\n name TEXT NOT NULL UNIQUE PRIMARY KEY,\n\tinternal BOOLEAN default false,\n visible BOOLEAN default true,\n repeatable BOOLEAN default false,\n\tdescription TEXT\n);\n\nCREATE TABLE IF NOT EXISTS directive_arguments (\n parent TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n\ttype_modifiers TEXT,\n default_value TEXT,\n FOREIGN KEY (parent) REFERENCES directives(name),\n PRIMARY KEY (parent, name),\n UNIQUE (parent, name)\n);\n\nCREATE TABLE IF NOT EXISTS directive_locations (\n directive TEXT NOT NULL,\n location TEXT NOT NULL CHECK (location IN ('QUERY', 'MUTATION', 'SUBSCRIPTION', 'FIELD', 'FRAGMENT_DEFINITION', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT', 'SCHEMA', 'SCALAR', 'OBJECT', 'FIELD_DEFINITION', 'ARGUMENT_DEFINITION', 'INTERFACE', 'UNION', 'ENUM', 'ENUM_VALUE', 'INPUT_OBJECT', 'INPUT_FIELD_DEFINITION')),\n FOREIGN KEY (directive) REFERENCES directives(name),\n PRIMARY KEY (directive, location)\n);\n\nCREATE TABLE IF NOT EXISTS document_variable_directives (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tparent INTEGER NOT NULL,\n\tdirective TEXT NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n\tFOREIGN KEY (parent) REFERENCES document_variables(id) ON DELETE CASCADE,\n\tFOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_variable_directive_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES document_variable_directives(id) ON DELETE CASCADE\n);\n\n-----------------------------------------------------------\n-- Document Tables\n-----------------------------------------------------------\n\nCREATE TABLE IF NOT EXISTS document_variables (\n \tid INTEGER PRIMARY KEY AUTOINCREMENT,\n document TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n type_modifiers TEXT,\n default_value INT,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n\n FOREIGN KEY (default_value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n UNIQUE (document, name)\n);\n\n-- this is pulled out separately from operations and fragments so foreign keys can be used\nCREATE TABLE IF NOT EXISTS documents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('query', 'mutation', 'subscription', 'fragment')),\n raw_document INTEGER,\n type_condition TEXT,\n hash TEXT,\n printed TEXT,\n\t\tinternal boolean default false,\n\t\tvisible boolean default true,\n\t\tprocessed boolean default false,\n FOREIGN KEY (type_condition) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (raw_document) REFERENCES raw_documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selections (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n field_name TEXT NOT NULL,\n\tkind TEXT NOT NULL CHECK (kind IN ('field', 'fragment', 'inline_fragment')),\n alias TEXT,\n type TEXT, -- should be something like User.Avatar\n fragment_ref TEXT, -- used when fragment arguments cause a hash to be inlined (removing the ability to track what the original fragment is)\n\t\tfragment_args JSON -- used to store the arguments that are used when fragment variables are expanded\n);\n\nCREATE TABLE IF NOT EXISTS selection_directives (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n selection_id INTEGER NOT NULL,\n directive TEXT NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n FOREIGN KEY (selection_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selection_directive_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n document INTEGER NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES selection_directives(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_directives (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tdocument int NOT NULL,\n\tdirective TEXT NOT NULL,\n\trow INTEGER NOT NULL,\n\tcolumn INTEGER NOT NULL,\n\tFOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n\tFOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_directive_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES document_directives(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selection_refs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent_id INTEGER,\n child_id INTEGER NOT NULL,\n path_index INTEGER NOT NULL,\n document INTEGER NOT NULL,\n\trow INTEGER NOT NULL,\n\tcolumn INTEGER NOT NULL,\n\tinternal BOOLEAN NOT NULL DEFAULT false,\n FOREIGN KEY (parent_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (child_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selection_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n selection_id INTEGER NOT NULL,\n document INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n field_argument TEXT NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (selection_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\n\nCREATE TABLE IF NOT EXISTS argument_values (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL CHECK (kind IN ('Variable', 'Int', 'Float', 'String', 'Block', 'Boolean', 'Null', 'Enum', 'List', 'Object')),\n raw TEXT NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n expected_type TEXT NOT NULL,\n expected_type_modifiers TEXT,\n document INTEGER NOT NULL,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS argument_value_children (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT,\n parent INTEGER NOT NULL,\n value INTEGER NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n document INTEGER NOT NULL,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS discovered_lists (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT,\n node_type TEXT NOT NULL,\n edge_type TEXT,\n connection_type TEXT NOT NULL,\n node INTEGER NOT NULL,\n page_size INTEGER NOT NULL,\n document INTEGER NOT NULL,\n mode TEXT NOT NULL,\n embedded BOOLEAN NOT NULL,\n target_type TEXT NOT NULL,\n connection BOOLEAN default false,\n list_field INTEGER NOT NULL,\n paginate TEXT,\n supports_forward BOOLEAN default false,\n supports_backward BOOLEAN default false,\n cursor_type TEXT,\n\n FOREIGN KEY (list_field) REFERENCES selections(id) ON DELETE CASCADE,\n\t FOREIGN KEY (node) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (node_type) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_dependencies (\n document INTEGER NOT NULL,\n depends_on TEXT NOT NULL,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n UNIQUE (document, depends_on)\n);\n\n-----------------------------------------------------------\n-- Indices\n-----------------------------------------------------------\n\n-- component_fields\nCREATE INDEX IF NOT EXISTS idx_component_fields_type_fields ON component_fields(type_field);\n\n-- discovered_lists\nCREATE INDEX IF NOT EXISTS idx_discovered_lists_document ON discovered_lists(document);\nCREATE INDEX IF NOT EXISTS idx_discovered_lists_node ON discovered_lists(node);\nCREATE INDEX IF NOT EXISTS idx_discovered_lists_list_field ON discovered_lists(list_field);\n-- note: no index on discovered_lists(connection) \u2014 boolean column, ~2 distinct values\n\n-- types\nCREATE INDEX IF NOT EXISTS idx_types_kind_operation ON types(kind, operation);\n-- note: no index on types(name) \u2014 covered by PRIMARY KEY UNIQUE\n\n-- documents\nCREATE INDEX IF NOT EXISTS idx_documents_kind ON documents(kind);\nCREATE INDEX IF NOT EXISTS idx_documents_type_condition ON documents(type_condition);\nCREATE INDEX IF NOT EXISTS idx_documents_raw_document ON documents(raw_document);\nCREATE INDEX IF NOT EXISTS idx_documents_name_kind ON documents(name, kind);\n\n-- raw_documents\nCREATE INDEX IF NOT EXISTS idx_raw_documents_current_task ON raw_documents(current_task);\n\n-- selections\nCREATE INDEX IF NOT EXISTS idx_selections_type ON selections(type);\nCREATE INDEX IF NOT EXISTS idx_selections_alias ON selections(alias);\nCREATE INDEX IF NOT EXISTS idx_selections_field_name_kind ON selections(field_name, kind);\n\n-- selection_refs: composite covers document-only lookups, so no separate single-column index needed\nCREATE INDEX IF NOT EXISTS idx_selection_refs_parent_id ON selection_refs(parent_id);\n\nCREATE INDEX IF NOT EXISTS idx_selection_refs_child_id ON selection_refs(child_id);\nCREATE INDEX IF NOT EXISTS idx_selection_refs_document_parent_id ON selection_refs(document, parent_id);\n\n-- selection_directives / selection_directive_arguments / selection_arguments\nCREATE INDEX IF NOT EXISTS idx_selection_directives_selection ON selection_directives(selection_id);\nCREATE INDEX IF NOT EXISTS idx_selection_directives_directive ON selection_directives(directive);\nCREATE INDEX IF NOT EXISTS idx_selection_directive_arguments_parent_name ON selection_directive_arguments(parent, name);\n-- note: no index on selection_directive_arguments(parent) alone \u2014 covered by (parent,name) composite\nCREATE INDEX IF NOT EXISTS idx_selection_directive_arguments_value ON selection_directive_arguments(value);\n-- document FK on selection_directive_arguments and selection_arguments: used in WHERE/JOIN in CollectDocuments\nCREATE INDEX IF NOT EXISTS idx_selection_directive_arguments_document ON selection_directive_arguments(document);\nCREATE INDEX IF NOT EXISTS idx_selection_arguments_document ON selection_arguments(document);\nCREATE INDEX IF NOT EXISTS idx_selection_arguments_selection ON selection_arguments(selection_id);\nCREATE INDEX IF NOT EXISTS idx_selection_arguments_value ON selection_arguments(value);\n\n-- type_fields / type_field_arguments\n-- note: no index on type_fields(id) or type_field_arguments(id) \u2014 covered by their TEXT PRIMARY KEYs\nCREATE INDEX IF NOT EXISTS idx_type_fields_parent ON type_fields(parent);\nCREATE INDEX IF NOT EXISTS idx_type_fields_name ON type_fields(name);\nCREATE INDEX IF NOT EXISTS idx_type_configs_name ON type_configs(name);\n\n-- possible_types\n-- note: no index on possible_types(type) \u2014 covered by PRIMARY KEY(type,member) leading column\nCREATE INDEX IF NOT EXISTS idx_possible_types_member ON possible_types(member);\n\n-- type_fields: type and document FK columns have no implicit index\nCREATE INDEX IF NOT EXISTS idx_type_fields_type ON type_fields(type);\nCREATE INDEX IF NOT EXISTS idx_type_fields_document ON type_fields(document);\n\n-- enum_values\n-- note: no index on enum_values(parent) or (parent,value) \u2014 both covered by UNIQUE(parent,value)\n\n-- document_directives / document_directive_arguments\nCREATE INDEX IF NOT EXISTS idx_document_directives_document ON document_directives(document);\nCREATE INDEX IF NOT EXISTS idx_document_directives_directive ON document_directives(directive);\nCREATE INDEX IF NOT EXISTS idx_document_directive_arguments_parent_name ON document_directive_arguments(parent, name);\n-- note: no index on document_directive_arguments(parent) alone \u2014 covered by (parent,name) composite\nCREATE INDEX IF NOT EXISTS idx_document_directive_arguments_value on document_directive_arguments(value);\n\n-- document_variables\n-- note: no index on document_variables(document,name) \u2014 covered by UNIQUE(document,name)\n-- default_value FK is used as a JOIN column in validate and fragmentArguments transforms\nCREATE INDEX IF NOT EXISTS idx_document_variables_default_value ON document_variables(default_value);\nCREATE INDEX IF NOT EXISTS idx_document_variables_document_id ON document_variables(document, id);\nCREATE INDEX IF NOT EXISTS idx_document_variables_document_type_modifiers_default ON document_variables(document, type_modifiers, default_value);\n\n-- document_variable_directives / document_variable_directive_arguments\nCREATE INDEX IF NOT EXISTS idx_document_variable_directives_parent ON document_variable_directives(parent);\nCREATE INDEX IF NOT EXISTS idx_document_variable_directives_directive ON document_variable_directives(directive);\nCREATE INDEX IF NOT EXISTS idx_document_variable_directive_arguments_parent ON document_variable_directive_arguments(parent);\n\n-- document_dependencies\n-- note: no index on document_dependencies(document) \u2014 covered by UNIQUE(document,depends_on) leading column\nCREATE INDEX IF NOT EXISTS idx_document_dependency_depends_on on document_dependencies(depends_on);\n\n-- argument_values: composite (document,id) covers document-only lookups\n-- note: no separate index on argument_values(document) alone\nCREATE INDEX IF NOT EXISTS idx_argument_values_kind_raw ON argument_values(kind, raw);\nCREATE INDEX IF NOT EXISTS idx_argument_values_document_id ON argument_values(document, id);\nCREATE INDEX IF NOT EXISTS idx_argument_values_expected_type_document ON argument_values(expected_type, document);\n\n-- argument_value_children: composite (parent,value) covers parent-only lookups\n-- note: no separate index on argument_value_children(parent) alone\nCREATE INDEX IF NOT EXISTS idx_argument_value_children_parent_value ON argument_value_children(parent, value);\nCREATE INDEX IF NOT EXISTS idx_argument_value_children_value ON argument_value_children(value);\n-- document FK: large table; index needed for efficient CASCADE DELETE from documents\nCREATE INDEX IF NOT EXISTS idx_argument_value_children_document ON argument_value_children(document);\n";
|
|
5
|
+
export declare const create_schema = "\nCREATE TABLE IF NOT EXISTS plugins (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n port INTEGER NOT NULL,\n hooks JSON NOT NULL,\n plugin_order TEXT CHECK (plugin_order IS NULL OR plugin_order IN ('before', 'after', 'core')),\n include_runtime TEXT,\n include_static_runtime TEXT,\n config JSON,\n\t config_module TEXT,\n\t\tclient_plugins JSON\n);\n\n-- Watch Schema Config\nCREATE TABLE IF NOT EXISTS watch_schema_config (\n url TEXT NOT NULL,\n headers JSON,\n interval INTEGER,\n timeout INTEGER\n);\n\n-- Router Config\nCREATE TABLE IF NOT EXISTS router_config (\n api_endpoint TEXT,\n redirect TEXT UNIQUE,\n session_keys TEXT NOT NULL UNIQUE,\n url TEXT,\n mutation TEXT UNIQUE\n);\n\n-- Runtime Scalar Definition\nCREATE TABLE IF NOT EXISTS runtime_scalar_definitions (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n type TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS component_fields (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tdocument INTEGER NOT NULL,\n type TEXT,\n\tprop TEXT,\n field TEXT,\n\tinline BOOLEAN default false,\n type_field TEXT,\n fragment TEXT,\n\tUNIQUE (document),\n\tFOREIGN KEY (document) REFERENCES raw_documents(id) ON DELETE CASCADE\n);\n\n-- Static Config (main config table)\nCREATE TABLE IF NOT EXISTS config (\n include JSON NOT NULL,\n exclude JSON NOT NULL,\n schema_path TEXT NOT NULL,\n definitions_path TEXT,\n cache_buffer_size INTEGER,\n default_cache_policy TEXT,\n default_partial BOOLEAN,\n default_lifetime INTEGER,\n default_list_position TEXT CHECK (default_list_position IN ('APPEND', 'PREPEND')),\n default_list_target TEXT CHECK (default_list_target IN ('ALL', 'NULL')),\n default_paginate_mode TEXT CHECK (default_paginate_mode IN ('Infinite', 'SinglePage')),\n suppress_pagination_deduplication BOOLEAN,\n log_level TEXT CHECK (log_level IN ('QUIET', 'FULL', 'SUMMARY', 'SHORT_SUMMARY')),\n default_fragment_masking BOOLEAN,\n default_keys JSON,\n persisted_queries_path TEXT NOT NULL,\n project_root TEXT,\n runtime_dir TEXT,\n\t\tpath TEXT\n);\n\nCREATE TABLE IF NOT EXISTS scalar_config (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n type TEXT NOT NULL,\n\tinput_types JSON,\n\tmodule TEXT,\n\tdefault_import BOOLEAN\n);\n\n-- Types configuration\nCREATE TABLE IF NOT EXISTS type_configs (\n name TEXT NOT NULL,\n keys JSON NOT NULL,\n\tresolve_query TEXT\n);\n\n-- A table of original document contents (to be populated by plugins)\nCREATE TABLE IF NOT EXISTS raw_documents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n offset_line INTEGER,\n offset_column INTEGER,\n filepath TEXT NOT NULL,\n content TEXT NOT NULL,\n current_task TEXT,\n loaded_with TEXT\n);\n\n-----------------------------------------------------------\n-- Schema Definition Tables\n-----------------------------------------------------------\n\nCREATE TABLE IF NOT EXISTS types (\n name TEXT NOT NULL PRIMARY KEY UNIQUE,\n kind TEXT NOT NULL CHECK (kind IN ('OBJECT', 'INTERFACE', 'UNION', 'ENUM', 'SCALAR', 'INPUT')),\n operation TEXT,\n\tdescription TEXT,\n\tinternal BOOLEAN default false,\n\tbuilt_in BOOLEAN default false\n);\n\nCREATE TABLE IF NOT EXISTS type_fields (\n id TEXT PRIMARY KEY, -- will be something like User.name so we don't have to look up the generated id\n parent TEXT NOT NULL, -- will be User\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n\t type_modifiers TEXT,\n default_value TEXT,\n description TEXT,\n\t internal BOOLEAN default false,\n document INT,\n\n FOREIGN KEY (document) REFERENCES raw_documents(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (type) REFERENCES types(name) ON DELETE CASCADE,\n UNIQUE (parent, name)\n);\n\nCREATE TABLE IF NOT EXISTS type_field_arguments (\n id TEXT PRIMARY KEY,\n field TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n type_modifiers TEXT,\n default_value TEXT,\n FOREIGN KEY (field) REFERENCES type_fields(id) ON DELETE CASCADE,\n UNIQUE (field, name)\n);\n\n\nCREATE TABLE IF NOT EXISTS enum_values (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent TEXT NOT NULL,\n value TEXT NOT NULL,\n description TEXT,\n FOREIGN KEY (parent) REFERENCES types(name) ON DELETE CASCADE,\n UNIQUE (parent, value)\n);\n\nCREATE TABLE IF NOT EXISTS possible_types (\n type TEXT NOT NULL,\n member TEXT NOT NULL,\n FOREIGN KEY (type) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (member) REFERENCES types(name) ON DELETE CASCADE,\n PRIMARY KEY (type, member)\n);\n\nCREATE TABLE IF NOT EXISTS directives (\n name TEXT NOT NULL UNIQUE PRIMARY KEY,\n\tinternal BOOLEAN default false,\n visible BOOLEAN default true,\n repeatable BOOLEAN default false,\n\tdescription TEXT\n);\n\nCREATE TABLE IF NOT EXISTS directive_arguments (\n parent TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n\ttype_modifiers TEXT,\n default_value TEXT,\n FOREIGN KEY (parent) REFERENCES directives(name),\n PRIMARY KEY (parent, name),\n UNIQUE (parent, name)\n);\n\nCREATE TABLE IF NOT EXISTS directive_locations (\n directive TEXT NOT NULL,\n location TEXT NOT NULL CHECK (location IN ('QUERY', 'MUTATION', 'SUBSCRIPTION', 'FIELD', 'FRAGMENT_DEFINITION', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT', 'SCHEMA', 'SCALAR', 'OBJECT', 'FIELD_DEFINITION', 'ARGUMENT_DEFINITION', 'INTERFACE', 'UNION', 'ENUM', 'ENUM_VALUE', 'INPUT_OBJECT', 'INPUT_FIELD_DEFINITION')),\n FOREIGN KEY (directive) REFERENCES directives(name),\n PRIMARY KEY (directive, location)\n);\n\nCREATE TABLE IF NOT EXISTS document_variable_directives (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tparent INTEGER NOT NULL,\n\tdirective TEXT NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n\tFOREIGN KEY (parent) REFERENCES document_variables(id) ON DELETE CASCADE,\n\tFOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_variable_directive_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES document_variable_directives(id) ON DELETE CASCADE\n);\n\n-----------------------------------------------------------\n-- Document Tables\n-----------------------------------------------------------\n\nCREATE TABLE IF NOT EXISTS document_variables (\n \tid INTEGER PRIMARY KEY AUTOINCREMENT,\n document TEXT NOT NULL,\n name TEXT NOT NULL,\n type TEXT NOT NULL,\n type_modifiers TEXT,\n default_value INT,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n\n FOREIGN KEY (default_value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n UNIQUE (document, name)\n);\n\n-- this is pulled out separately from operations and fragments so foreign keys can be used\nCREATE TABLE IF NOT EXISTS documents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('query', 'mutation', 'subscription', 'fragment')),\n raw_document INTEGER,\n type_condition TEXT,\n hash TEXT,\n printed TEXT,\n\t\tinternal boolean default false,\n\t\tvisible boolean default true,\n\t\tprocessed boolean default false,\n FOREIGN KEY (type_condition) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (raw_document) REFERENCES raw_documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selections (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n field_name TEXT NOT NULL,\n\tkind TEXT NOT NULL CHECK (kind IN ('field', 'fragment', 'inline_fragment')),\n alias TEXT,\n type TEXT, -- should be something like User.Avatar\n fragment_ref TEXT, -- used when fragment arguments cause a hash to be inlined (removing the ability to track what the original fragment is)\n\t\tfragment_args JSON -- used to store the arguments that are used when fragment variables are expanded\n);\n\nCREATE TABLE IF NOT EXISTS selection_directives (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n selection_id INTEGER NOT NULL,\n directive TEXT NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n FOREIGN KEY (selection_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selection_directive_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n document INTEGER NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES selection_directives(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_directives (\n\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n\tdocument int NOT NULL,\n\tdirective TEXT NOT NULL,\n\trow INTEGER NOT NULL,\n\tcolumn INTEGER NOT NULL,\n\tFOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n\tFOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_directive_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES document_directives(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selection_refs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parent_id INTEGER,\n child_id INTEGER NOT NULL,\n path_index INTEGER NOT NULL,\n document INTEGER NOT NULL,\n\trow INTEGER NOT NULL,\n\tcolumn INTEGER NOT NULL,\n\tinternal BOOLEAN NOT NULL DEFAULT false,\n FOREIGN KEY (parent_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (child_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS selection_arguments (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n selection_id INTEGER NOT NULL,\n document INTEGER NOT NULL,\n name TEXT NOT NULL,\n value INTEGER NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n field_argument TEXT NOT NULL,\n\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (selection_id) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\n\nCREATE TABLE IF NOT EXISTS argument_values (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n kind TEXT NOT NULL CHECK (kind IN ('Variable', 'Int', 'Float', 'String', 'Block', 'Boolean', 'Null', 'Enum', 'List', 'Object')),\n raw TEXT NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n expected_type TEXT NOT NULL,\n expected_type_modifiers TEXT,\n document INTEGER NOT NULL,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS argument_value_children (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT,\n parent INTEGER NOT NULL,\n value INTEGER NOT NULL,\n row INTEGER NOT NULL,\n column INTEGER NOT NULL,\n document INTEGER NOT NULL,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n FOREIGN KEY (parent) REFERENCES argument_values(id) ON DELETE CASCADE,\n FOREIGN KEY (value) REFERENCES argument_values(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS discovered_lists (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT,\n node_type TEXT NOT NULL,\n edge_type TEXT,\n connection_type TEXT NOT NULL,\n node INTEGER NOT NULL,\n page_size INTEGER NOT NULL,\n document INTEGER NOT NULL,\n mode TEXT NOT NULL,\n embedded BOOLEAN NOT NULL,\n target_type TEXT NOT NULL,\n connection BOOLEAN default false,\n list_field INTEGER NOT NULL,\n paginate TEXT,\n supports_forward BOOLEAN default false,\n supports_backward BOOLEAN default false,\n cursor_type TEXT,\n\n FOREIGN KEY (list_field) REFERENCES selections(id) ON DELETE CASCADE,\n\t FOREIGN KEY (node) REFERENCES selections(id) ON DELETE CASCADE,\n FOREIGN KEY (node_type) REFERENCES types(name) ON DELETE CASCADE,\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE\n);\n\nCREATE TABLE IF NOT EXISTS document_dependencies (\n document INTEGER NOT NULL,\n depends_on TEXT NOT NULL,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n UNIQUE (document, depends_on)\n);\n\n-----------------------------------------------------------\n-- Indices\n-----------------------------------------------------------\n\n-- component_fields\nCREATE INDEX IF NOT EXISTS idx_component_fields_type_fields ON component_fields(type_field);\n\n-- discovered_lists\nCREATE INDEX IF NOT EXISTS idx_discovered_lists_document ON discovered_lists(document);\nCREATE INDEX IF NOT EXISTS idx_discovered_lists_node ON discovered_lists(node);\nCREATE INDEX IF NOT EXISTS idx_discovered_lists_list_field ON discovered_lists(list_field);\n-- note: no index on discovered_lists(connection) \u2014 boolean column, ~2 distinct values\n\n-- types\nCREATE INDEX IF NOT EXISTS idx_types_kind_operation ON types(kind, operation);\n-- note: no index on types(name) \u2014 covered by PRIMARY KEY UNIQUE\n\n-- documents\nCREATE INDEX IF NOT EXISTS idx_documents_kind ON documents(kind);\nCREATE INDEX IF NOT EXISTS idx_documents_type_condition ON documents(type_condition);\nCREATE INDEX IF NOT EXISTS idx_documents_raw_document ON documents(raw_document);\nCREATE INDEX IF NOT EXISTS idx_documents_name_kind ON documents(name, kind);\n\n-- raw_documents\nCREATE INDEX IF NOT EXISTS idx_raw_documents_current_task ON raw_documents(current_task);\nCREATE INDEX IF NOT EXISTS idx_raw_documents_filepath ON raw_documents(filepath);\n\n-- selections\nCREATE INDEX IF NOT EXISTS idx_selections_type ON selections(type);\nCREATE INDEX IF NOT EXISTS idx_selections_alias ON selections(alias);\nCREATE INDEX IF NOT EXISTS idx_selections_field_name_kind ON selections(field_name, kind);\n\n-- selection_refs: composite covers document-only lookups, so no separate single-column index needed\nCREATE INDEX IF NOT EXISTS idx_selection_refs_parent_id ON selection_refs(parent_id);\n\nCREATE INDEX IF NOT EXISTS idx_selection_refs_child_id ON selection_refs(child_id);\nCREATE INDEX IF NOT EXISTS idx_selection_refs_document_parent_id ON selection_refs(document, parent_id);\n\n-- selection_directives / selection_directive_arguments / selection_arguments\nCREATE INDEX IF NOT EXISTS idx_selection_directives_selection ON selection_directives(selection_id);\nCREATE INDEX IF NOT EXISTS idx_selection_directives_directive ON selection_directives(directive);\nCREATE INDEX IF NOT EXISTS idx_selection_directive_arguments_parent_name ON selection_directive_arguments(parent, name);\n-- note: no index on selection_directive_arguments(parent) alone \u2014 covered by (parent,name) composite\nCREATE INDEX IF NOT EXISTS idx_selection_directive_arguments_value ON selection_directive_arguments(value);\n-- document FK on selection_directive_arguments and selection_arguments: used in WHERE/JOIN in CollectDocuments\nCREATE INDEX IF NOT EXISTS idx_selection_directive_arguments_document ON selection_directive_arguments(document);\nCREATE INDEX IF NOT EXISTS idx_selection_arguments_document ON selection_arguments(document);\nCREATE INDEX IF NOT EXISTS idx_selection_arguments_selection ON selection_arguments(selection_id);\nCREATE INDEX IF NOT EXISTS idx_selection_arguments_value ON selection_arguments(value);\n\n-- type_fields / type_field_arguments\n-- note: no index on type_fields(id) or type_field_arguments(id) \u2014 covered by their TEXT PRIMARY KEYs\nCREATE INDEX IF NOT EXISTS idx_type_fields_parent ON type_fields(parent);\nCREATE INDEX IF NOT EXISTS idx_type_fields_name ON type_fields(name);\nCREATE INDEX IF NOT EXISTS idx_type_configs_name ON type_configs(name);\n\n-- possible_types\n-- note: no index on possible_types(type) \u2014 covered by PRIMARY KEY(type,member) leading column\nCREATE INDEX IF NOT EXISTS idx_possible_types_member ON possible_types(member);\n\n-- type_fields: type and document FK columns have no implicit index\nCREATE INDEX IF NOT EXISTS idx_type_fields_type ON type_fields(type);\nCREATE INDEX IF NOT EXISTS idx_type_fields_document ON type_fields(document);\n\n-- enum_values\n-- note: no index on enum_values(parent) or (parent,value) \u2014 both covered by UNIQUE(parent,value)\n\n-- document_directives / document_directive_arguments\nCREATE INDEX IF NOT EXISTS idx_document_directives_document ON document_directives(document);\nCREATE INDEX IF NOT EXISTS idx_document_directives_directive ON document_directives(directive);\nCREATE INDEX IF NOT EXISTS idx_document_directive_arguments_parent_name ON document_directive_arguments(parent, name);\n-- note: no index on document_directive_arguments(parent) alone \u2014 covered by (parent,name) composite\nCREATE INDEX IF NOT EXISTS idx_document_directive_arguments_value on document_directive_arguments(value);\n\n-- document_variables\n-- note: no index on document_variables(document,name) \u2014 covered by UNIQUE(document,name)\n-- default_value FK is used as a JOIN column in validate and fragmentArguments transforms\nCREATE INDEX IF NOT EXISTS idx_document_variables_default_value ON document_variables(default_value);\nCREATE INDEX IF NOT EXISTS idx_document_variables_document_id ON document_variables(document, id);\nCREATE INDEX IF NOT EXISTS idx_document_variables_document_type_modifiers_default ON document_variables(document, type_modifiers, default_value);\n\n-- document_variable_directives / document_variable_directive_arguments\nCREATE INDEX IF NOT EXISTS idx_document_variable_directives_parent ON document_variable_directives(parent);\nCREATE INDEX IF NOT EXISTS idx_document_variable_directives_directive ON document_variable_directives(directive);\nCREATE INDEX IF NOT EXISTS idx_document_variable_directive_arguments_parent ON document_variable_directive_arguments(parent);\n\n-- document_dependencies\n-- note: no index on document_dependencies(document) \u2014 covered by UNIQUE(document,depends_on) leading column\nCREATE INDEX IF NOT EXISTS idx_document_dependency_depends_on on document_dependencies(depends_on);\n\n-- argument_values: composite (document,id) covers document-only lookups\n-- note: no separate index on argument_values(document) alone\nCREATE INDEX IF NOT EXISTS idx_argument_values_kind_raw ON argument_values(kind, raw);\nCREATE INDEX IF NOT EXISTS idx_argument_values_document_id ON argument_values(document, id);\nCREATE INDEX IF NOT EXISTS idx_argument_values_expected_type_document ON argument_values(expected_type, document);\n\n-- argument_value_children: composite (parent,value) covers parent-only lookups\n-- note: no separate index on argument_value_children(parent) alone\nCREATE INDEX IF NOT EXISTS idx_argument_value_children_parent_value ON argument_value_children(parent, value);\nCREATE INDEX IF NOT EXISTS idx_argument_value_children_value ON argument_value_children(value);\n-- document FK: large table; index needed for efficient CASCADE DELETE from documents\nCREATE INDEX IF NOT EXISTS idx_argument_value_children_document ON argument_value_children(document);\n";
|
|
6
6
|
export declare function write_config(db: Db, config: Config, invoke_hook: (plugin: string, hook: string, args: Record<string, any>) => Promise<Record<string, any>>, plugins: Array<PluginSpec>, mode: string, logger?: Logger): Promise<void>;
|
package/build/lib/database.js
CHANGED
|
@@ -407,6 +407,7 @@ CREATE INDEX IF NOT EXISTS idx_documents_name_kind ON documents(name, kind);
|
|
|
407
407
|
|
|
408
408
|
-- raw_documents
|
|
409
409
|
CREATE INDEX IF NOT EXISTS idx_raw_documents_current_task ON raw_documents(current_task);
|
|
410
|
+
CREATE INDEX IF NOT EXISTS idx_raw_documents_filepath ON raw_documents(filepath);
|
|
410
411
|
|
|
411
412
|
-- selections
|
|
412
413
|
CREATE INDEX IF NOT EXISTS idx_selections_type ON selections(type);
|
package/build/lib/plugins.js
CHANGED
|
@@ -10,45 +10,49 @@ async function plugin_path(plugin_name, config_path, preferWasm = false) {
|
|
|
10
10
|
}
|
|
11
11
|
return { executable, directory: path.dirname(executable) };
|
|
12
12
|
}
|
|
13
|
+
if (process.versions.pnp) {
|
|
14
|
+
const { findPnpApi } = require("node:module");
|
|
15
|
+
const pnp = findPnpApi(config_path);
|
|
16
|
+
return pnp.resolveRequest(plugin_name, config_path, {
|
|
17
|
+
conditions: /* @__PURE__ */ new Set(["import"])
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
let plugin_dir;
|
|
13
21
|
try {
|
|
14
|
-
|
|
15
|
-
const { findPnpApi } = require("node:module");
|
|
16
|
-
const pnp = findPnpApi(config_path);
|
|
17
|
-
return pnp.resolveRequest(plugin_name, config_path, {
|
|
18
|
-
conditions: /* @__PURE__ */ new Set(["import"])
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
const plugin_dir = find_module(plugin_name, config_path);
|
|
22
|
-
const package_json_src = await fs.readFile(path.join(plugin_dir, "package.json"));
|
|
23
|
-
if (!package_json_src) {
|
|
24
|
-
throw new Error("There is no package.json.");
|
|
25
|
-
}
|
|
26
|
-
const package_json = JSON.parse(package_json_src);
|
|
27
|
-
if (!package_json.bin) {
|
|
28
|
-
throw new Error("There is no bin defined.");
|
|
29
|
-
}
|
|
30
|
-
const bin_value = typeof package_json.bin === "string" ? package_json.bin : Object.values(package_json.bin)[0];
|
|
31
|
-
const native_bin = path.join(plugin_dir, bin_value);
|
|
32
|
-
if (preferWasm) {
|
|
33
|
-
try {
|
|
34
|
-
const wasm_dir = find_module(`${plugin_name}-wasm`, config_path);
|
|
35
|
-
return {
|
|
36
|
-
executable: path.join(wasm_dir, "bin", `${plugin_name}.wasm`),
|
|
37
|
-
directory: plugin_dir
|
|
38
|
-
};
|
|
39
|
-
} catch {
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
executable: native_bin,
|
|
44
|
-
directory: plugin_dir
|
|
45
|
-
};
|
|
22
|
+
plugin_dir = find_module(plugin_name, config_path);
|
|
46
23
|
} catch (e) {
|
|
47
|
-
|
|
48
|
-
`Could not find plugin: ${plugin_name}. Are you sure
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Could not find plugin: ${plugin_name}. Are you sure it's installed? If so, please open a ticket on GitHub. ${e}`
|
|
49
26
|
);
|
|
50
|
-
throw err;
|
|
51
27
|
}
|
|
28
|
+
const package_json_src = await fs.readFile(path.join(plugin_dir, "package.json"));
|
|
29
|
+
if (!package_json_src) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Found package '${plugin_name}' but could not read its package.json at ${plugin_dir}.`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const package_json = JSON.parse(package_json_src);
|
|
35
|
+
if (!package_json.bin) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Found package '${plugin_name}' but it has no bin field in its package.json. Houdini plugins are executables \u2014 add a "bin" entry pointing to the plugin's entry point. If this is a local monorepo package, this is the most likely cause.`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const bin_value = typeof package_json.bin === "string" ? package_json.bin : Object.values(package_json.bin)[0];
|
|
41
|
+
const native_bin = path.join(plugin_dir, bin_value);
|
|
42
|
+
if (preferWasm) {
|
|
43
|
+
try {
|
|
44
|
+
const wasm_dir = find_module(`${plugin_name}-wasm`, config_path);
|
|
45
|
+
return {
|
|
46
|
+
executable: path.join(wasm_dir, "bin", `${plugin_name}.wasm`),
|
|
47
|
+
directory: plugin_dir
|
|
48
|
+
};
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
executable: native_bin,
|
|
54
|
+
directory: plugin_dir
|
|
55
|
+
};
|
|
52
56
|
}
|
|
53
57
|
function find_module(pkg, config_path) {
|
|
54
58
|
try {
|
package/build/node/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Db } from '../lib/db.js';
|
|
2
2
|
export type { Db } from '../lib/db.js';
|
|
3
|
-
export type PipelineHook = 'config' | 'afterLoad' | 'schema' | 'extractDocuments' | 'afterExtract' | 'beforeValidate' | 'validate' | 'afterValidate' | 'beforeGenerate' | 'generateDocuments' | 'generateRuntime' | 'afterGenerate';
|
|
3
|
+
export type PipelineHook = 'config' | 'afterLoad' | 'schema' | 'extractDocuments' | 'afterExtract' | 'beforeValidate' | 'validate' | 'afterValidate' | 'beforeGenerate' | 'generateDocuments' | 'generateRuntime' | 'afterGenerate' | 'environment' | 'indexFile';
|
|
4
4
|
export type PluginContext = {
|
|
5
5
|
taskId: string;
|
|
6
6
|
pluginDirectory: string;
|
|
@@ -9,12 +9,16 @@ export type PluginContext = {
|
|
|
9
9
|
parallel?: boolean;
|
|
10
10
|
}): Promise<Record<string, any>>;
|
|
11
11
|
};
|
|
12
|
-
export type
|
|
12
|
+
export type TransformFn = (source: string, content: string) => Promise<string> | string;
|
|
13
|
+
export type HookHandler = (ctx: PluginContext, payload: Record<string, any>) => Promise<Record<string, any> | string[] | string | undefined> | Record<string, any> | string[] | string | undefined;
|
|
13
14
|
export type NodePluginConfig = {
|
|
14
15
|
name: string;
|
|
15
16
|
order: 'before' | 'after' | 'core';
|
|
16
17
|
hooks: Partial<Record<PipelineHook, HookHandler>>;
|
|
17
18
|
includeRuntime?: string;
|
|
19
|
+
staticRuntime?: string;
|
|
20
|
+
transformRuntime?: TransformFn;
|
|
21
|
+
transformStaticRuntime?: TransformFn;
|
|
18
22
|
configModule?: string;
|
|
19
23
|
clientPlugins?: Record<string, any>;
|
|
20
24
|
};
|
package/build/node/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
1
2
|
import http from "node:http";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
2
4
|
import { createInterface } from "node:readline";
|
|
3
|
-
import { openDb } from "../lib/db.js";
|
|
4
5
|
import { WebSocketServer } from "ws";
|
|
6
|
+
import { openDb } from "../lib/db.js";
|
|
5
7
|
class PluginError extends Error {
|
|
6
8
|
detail;
|
|
7
9
|
locations;
|
|
@@ -22,14 +24,17 @@ function plugin(config) {
|
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
async function runStdio(config, databasePath, pluginKey) {
|
|
27
|
+
const resolvedName = pluginKey || config.name;
|
|
25
28
|
const { pending, invokeCounter, rl } = makeStdioChannel();
|
|
29
|
+
const wireHooks = registeredHookNames(config).map(toWireName);
|
|
26
30
|
const reg = {
|
|
27
31
|
type: "register",
|
|
28
|
-
name:
|
|
29
|
-
hooks:
|
|
32
|
+
name: resolvedName,
|
|
33
|
+
hooks: wireHooks,
|
|
30
34
|
order: config.order
|
|
31
35
|
};
|
|
32
36
|
if (config.includeRuntime !== void 0) reg.includeRuntime = config.includeRuntime;
|
|
37
|
+
if (config.staticRuntime !== void 0) reg.includeStaticRuntime = config.staticRuntime;
|
|
33
38
|
if (config.configModule !== void 0) reg.configModule = config.configModule;
|
|
34
39
|
if (config.clientPlugins !== void 0) reg.clientPlugins = JSON.stringify(config.clientPlugins);
|
|
35
40
|
stdioWrite(reg);
|
|
@@ -49,7 +54,7 @@ async function runStdio(config, databasePath, pluginKey) {
|
|
|
49
54
|
db: db2,
|
|
50
55
|
invokeHook: makeInvokeHook(pending, invokeCounter, msg.taskId ?? "")
|
|
51
56
|
};
|
|
52
|
-
await dispatch(config, msg, ctx, (response) => stdioWrite(response));
|
|
57
|
+
await dispatch(config, resolvedName, msg, ctx, (response) => stdioWrite(response));
|
|
53
58
|
} else if (msg.type === "invoke_result") {
|
|
54
59
|
resolveInvoke(pending, msg);
|
|
55
60
|
}
|
|
@@ -69,8 +74,9 @@ async function runWebSocket(config, databasePath, pluginKey) {
|
|
|
69
74
|
process.stderr.write("node plugin: --database path is required in websocket mode\n");
|
|
70
75
|
process.exit(1);
|
|
71
76
|
}
|
|
77
|
+
const resolvedName = pluginKey || config.name;
|
|
72
78
|
const db = await openDb(databasePath);
|
|
73
|
-
const wireHooks =
|
|
79
|
+
const wireHooks = registeredHookNames(config).map(toWireName);
|
|
74
80
|
const wsInvokeHook = () => {
|
|
75
81
|
throw new Error("invokeHook is not supported in websocket transport");
|
|
76
82
|
};
|
|
@@ -98,7 +104,7 @@ async function runWebSocket(config, databasePath, pluginKey) {
|
|
|
98
104
|
db,
|
|
99
105
|
invokeHook: wsInvokeHook
|
|
100
106
|
};
|
|
101
|
-
await dispatch(config, msg, ctx, (response) => {
|
|
107
|
+
await dispatch(config, resolvedName, msg, ctx, (response) => {
|
|
102
108
|
if (response.error) {
|
|
103
109
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
104
110
|
res.end(JSON.stringify(response.error));
|
|
@@ -134,7 +140,13 @@ async function runWebSocket(config, databasePath, pluginKey) {
|
|
|
134
140
|
db,
|
|
135
141
|
invokeHook: wsInvokeHook
|
|
136
142
|
};
|
|
137
|
-
await dispatch(
|
|
143
|
+
await dispatch(
|
|
144
|
+
config,
|
|
145
|
+
resolvedName,
|
|
146
|
+
msg,
|
|
147
|
+
ctx,
|
|
148
|
+
(response) => ws.send(JSON.stringify(response))
|
|
149
|
+
);
|
|
138
150
|
});
|
|
139
151
|
ws.on("close", () => {
|
|
140
152
|
db.close();
|
|
@@ -150,14 +162,15 @@ async function runWebSocket(config, databasePath, pluginKey) {
|
|
|
150
162
|
server.listen(0, () => {
|
|
151
163
|
const port = server.address().port;
|
|
152
164
|
db.run(
|
|
153
|
-
`INSERT INTO plugins (name, hooks, port, plugin_order, include_runtime, config_module, client_plugins)
|
|
154
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
165
|
+
`INSERT INTO plugins (name, hooks, port, plugin_order, include_runtime, include_static_runtime, config_module, client_plugins)
|
|
166
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
155
167
|
[
|
|
156
|
-
|
|
168
|
+
resolvedName,
|
|
157
169
|
JSON.stringify(wireHooks),
|
|
158
170
|
port,
|
|
159
171
|
config.order,
|
|
160
172
|
config.includeRuntime ?? null,
|
|
173
|
+
config.staticRuntime ?? null,
|
|
161
174
|
config.configModule ?? null,
|
|
162
175
|
config.clientPlugins ? JSON.stringify(config.clientPlugins) : null
|
|
163
176
|
]
|
|
@@ -167,6 +180,153 @@ async function runWebSocket(config, databasePath, pluginKey) {
|
|
|
167
180
|
process.on("SIGINT", () => shutdown(db, server));
|
|
168
181
|
process.on("SIGTERM", () => shutdown(db, server));
|
|
169
182
|
}
|
|
183
|
+
async function dispatch(config, pluginName, msg, ctx, send) {
|
|
184
|
+
const normalized = msg.hook.toLowerCase();
|
|
185
|
+
const payload = msg.payload ?? {};
|
|
186
|
+
try {
|
|
187
|
+
let result;
|
|
188
|
+
switch (normalized) {
|
|
189
|
+
case "config": {
|
|
190
|
+
const handler = config.hooks.config;
|
|
191
|
+
result = handler ? await handler(ctx, payload) : void 0;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case "afterload": {
|
|
195
|
+
result = await dispatchAfterLoad(config, pluginName, ctx, payload);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case "generateruntime": {
|
|
199
|
+
result = await dispatchGenerateRuntime(config, pluginName, ctx, payload);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case "indexfile": {
|
|
203
|
+
await dispatchIndexFile(config, pluginName, ctx, payload);
|
|
204
|
+
result = void 0;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
default: {
|
|
208
|
+
const hookKey = Object.keys(config.hooks).find(
|
|
209
|
+
(k) => k.toLowerCase() === normalized
|
|
210
|
+
);
|
|
211
|
+
const handler = hookKey ? config.hooks[hookKey] : void 0;
|
|
212
|
+
if (!handler) {
|
|
213
|
+
send({
|
|
214
|
+
id: msg.id,
|
|
215
|
+
type: "response",
|
|
216
|
+
error: { message: `no handler for hook ${msg.hook}` }
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
result = await handler(ctx, payload);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
send({ id: msg.id, type: "response", result: result ?? {} });
|
|
224
|
+
} catch (err) {
|
|
225
|
+
send({ id: msg.id, type: "response", error: serializeError(err) });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function dispatchAfterLoad(config, pluginName, ctx, payload) {
|
|
229
|
+
if (config.hooks.config) {
|
|
230
|
+
const defaults = await config.hooks.config(ctx, payload);
|
|
231
|
+
if (defaults !== void 0) {
|
|
232
|
+
ctx.db.run("UPDATE plugins SET config = $config WHERE name = $name", {
|
|
233
|
+
$config: JSON.stringify(defaults),
|
|
234
|
+
$name: pluginName
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (config.staticRuntime) {
|
|
239
|
+
const { projectRoot, runtimeDir } = readProjectConfig(ctx.db);
|
|
240
|
+
const src = join(ctx.pluginDirectory, config.staticRuntime);
|
|
241
|
+
const dst = pluginStaticRuntimeDir(projectRoot, runtimeDir, pluginName);
|
|
242
|
+
await recursiveCopy(src, dst, config.transformStaticRuntime);
|
|
243
|
+
}
|
|
244
|
+
if (config.hooks.afterLoad) {
|
|
245
|
+
return await config.hooks.afterLoad(ctx, payload);
|
|
246
|
+
}
|
|
247
|
+
return void 0;
|
|
248
|
+
}
|
|
249
|
+
async function dispatchGenerateRuntime(config, pluginName, ctx, payload) {
|
|
250
|
+
const paths = [];
|
|
251
|
+
if (config.includeRuntime !== void 0) {
|
|
252
|
+
const { projectRoot, runtimeDir } = readProjectConfig(ctx.db);
|
|
253
|
+
const src = join(ctx.pluginDirectory, config.includeRuntime);
|
|
254
|
+
const dst = pluginRuntimeDir(projectRoot, runtimeDir, pluginName);
|
|
255
|
+
const copied = await recursiveCopy(src, dst, config.transformRuntime);
|
|
256
|
+
paths.push(...copied);
|
|
257
|
+
}
|
|
258
|
+
if (config.hooks.generateRuntime) {
|
|
259
|
+
const result = await config.hooks.generateRuntime(ctx, payload);
|
|
260
|
+
if (Array.isArray(result)) {
|
|
261
|
+
paths.push(...result);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return paths;
|
|
265
|
+
}
|
|
266
|
+
async function dispatchIndexFile(config, pluginName, ctx, payload) {
|
|
267
|
+
if (!config.hooks.indexFile) return;
|
|
268
|
+
const { projectRoot, runtimeDir } = readProjectConfig(ctx.db);
|
|
269
|
+
const targetPath = join(projectRoot, runtimeDir, "index.ts");
|
|
270
|
+
const content = await config.hooks.indexFile(ctx, { ...payload, filepath: targetPath });
|
|
271
|
+
if (typeof content === "string" && content) {
|
|
272
|
+
const existing = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
|
|
273
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
274
|
+
writeFileSync(targetPath, existing + "\n" + content, "utf8");
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function readProjectConfig(db) {
|
|
278
|
+
const row = db.get(
|
|
279
|
+
"SELECT project_root, runtime_dir FROM config LIMIT 1"
|
|
280
|
+
);
|
|
281
|
+
return { projectRoot: row?.project_root ?? "", runtimeDir: row?.runtime_dir ?? "" };
|
|
282
|
+
}
|
|
283
|
+
function pluginRuntimeDir(projectRoot, runtimeDir, name) {
|
|
284
|
+
if (name === "houdini-core") {
|
|
285
|
+
return join(projectRoot, runtimeDir, "runtime");
|
|
286
|
+
}
|
|
287
|
+
return join(projectRoot, runtimeDir, "plugins", name, "runtime");
|
|
288
|
+
}
|
|
289
|
+
function pluginStaticRuntimeDir(projectRoot, runtimeDir, name) {
|
|
290
|
+
return join(projectRoot, runtimeDir, "plugins", name, "static");
|
|
291
|
+
}
|
|
292
|
+
async function recursiveCopy(src, dst, transform) {
|
|
293
|
+
const written = [];
|
|
294
|
+
if (!existsSync(src)) return written;
|
|
295
|
+
const walk = async (srcDir, dstDir) => {
|
|
296
|
+
mkdirSync(dstDir, { recursive: true });
|
|
297
|
+
for (const entry of readdirSync(srcDir)) {
|
|
298
|
+
const srcPath = join(srcDir, entry);
|
|
299
|
+
const dstPath = join(dstDir, entry);
|
|
300
|
+
if (statSync(srcPath).isDirectory()) {
|
|
301
|
+
await walk(srcPath, dstPath);
|
|
302
|
+
} else {
|
|
303
|
+
let content = readFileSync(srcPath, "utf8");
|
|
304
|
+
if (transform) content = await transform(srcPath, content);
|
|
305
|
+
mkdirSync(dirname(dstPath), { recursive: true });
|
|
306
|
+
writeFileSync(dstPath, content, "utf8");
|
|
307
|
+
written.push(dstPath);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
await walk(src, dst);
|
|
312
|
+
return written;
|
|
313
|
+
}
|
|
314
|
+
function registeredHookNames(config) {
|
|
315
|
+
const names = /* @__PURE__ */ new Set();
|
|
316
|
+
names.add("config");
|
|
317
|
+
if (config.staticRuntime || config.hooks.afterLoad || config.hooks.config) {
|
|
318
|
+
names.add("afterLoad");
|
|
319
|
+
}
|
|
320
|
+
if (config.includeRuntime !== void 0 || config.hooks.generateRuntime || config.configModule !== void 0) {
|
|
321
|
+
names.add("generateRuntime");
|
|
322
|
+
}
|
|
323
|
+
for (const key of Object.keys(config.hooks)) {
|
|
324
|
+
if (typeof config.hooks[key] === "function") {
|
|
325
|
+
names.add(key);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return Array.from(names);
|
|
329
|
+
}
|
|
170
330
|
function makeStdioChannel() {
|
|
171
331
|
const pending = /* @__PURE__ */ new Map();
|
|
172
332
|
const invokeCounter = { value: 0 };
|
|
@@ -206,32 +366,6 @@ function shutdown(db, server) {
|
|
|
206
366
|
server.close();
|
|
207
367
|
process.exit(0);
|
|
208
368
|
}
|
|
209
|
-
async function dispatch(config, msg, ctx, send) {
|
|
210
|
-
const normalizedHook = msg.hook.toLowerCase();
|
|
211
|
-
const hookKey = Object.keys(config.hooks).find(
|
|
212
|
-
(k) => k.toLowerCase() === normalizedHook
|
|
213
|
-
);
|
|
214
|
-
const handler = hookKey ? config.hooks[hookKey] : void 0;
|
|
215
|
-
if (!handler) {
|
|
216
|
-
send({
|
|
217
|
-
id: msg.id,
|
|
218
|
-
type: "response",
|
|
219
|
-
error: { message: `no handler for hook ${msg.hook}` }
|
|
220
|
-
});
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
try {
|
|
224
|
-
const result = await handler(ctx, msg.payload ?? {});
|
|
225
|
-
send({ id: msg.id, type: "response", result: result ?? {} });
|
|
226
|
-
} catch (err) {
|
|
227
|
-
send({ id: msg.id, type: "response", error: serializeError(err) });
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function hookKeys(config) {
|
|
231
|
-
return Object.keys(config.hooks).filter(
|
|
232
|
-
(k) => typeof config.hooks[k] === "function"
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
369
|
function parseArgs() {
|
|
236
370
|
const argv = process.argv;
|
|
237
371
|
let transport = "websocket";
|
package/build/package.json
CHANGED
|
@@ -331,9 +331,7 @@ class CacheInternal {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
|
-
if (updates && applyUpdates?.
|
|
335
|
-
newValue = previousValue;
|
|
336
|
-
} else if (updates && applyUpdates?.includes("append") && ["startCursor", "hasPreviousPage"].includes(key)) {
|
|
334
|
+
if (updates && applyUpdates?.some((op) => !updates.includes(op))) {
|
|
337
335
|
newValue = previousValue;
|
|
338
336
|
}
|
|
339
337
|
const valueChanged = !deepEquals(newValue, previousValue);
|
package/build/vite/hmr.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Plugin as VitePlugin
|
|
2
|
-
import type { VitePluginContext } from './index.js';
|
|
1
|
+
import type { HmrContext, Plugin as VitePlugin } from 'vite';
|
|
3
2
|
import { type CompilerProxy } from '../lib/index.js';
|
|
3
|
+
import type { VitePluginContext } from './index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Houdini Vite HMR Plugin
|
|
6
6
|
*
|
|
@@ -20,7 +20,7 @@ export declare let compiler: CompilerProxy;
|
|
|
20
20
|
export declare function document_hmr(ctx: VitePluginContext): VitePlugin;
|
|
21
21
|
type BatchCallback = (filesWithContent: Record<string, string>, deletedFiles: string[], batchId: string) => void | Promise<void>;
|
|
22
22
|
export declare function createDebounceHmr(debounceMs?: number): {
|
|
23
|
-
queueUpdate(ctx: HmrContext, callback: BatchCallback): void;
|
|
23
|
+
queueUpdate(ctx: HmrContext, preReadContent: string | null, callback: BatchCallback): void;
|
|
24
24
|
queueDelete(relativePath: string, callback: BatchCallback): void;
|
|
25
25
|
};
|
|
26
26
|
export {};
|
package/build/vite/hmr.js
CHANGED
|
@@ -5,6 +5,12 @@ function document_hmr(ctx) {
|
|
|
5
5
|
const debounceHmr = createDebounceHmr(50);
|
|
6
6
|
let config;
|
|
7
7
|
const ownWrites = /* @__PURE__ */ new Map();
|
|
8
|
+
let generatedDir;
|
|
9
|
+
let rootPrefix;
|
|
10
|
+
const cleanupFiles = /* @__PURE__ */ new Set();
|
|
11
|
+
function isManifestFile(filepath) {
|
|
12
|
+
return (filepath.split("/").pop() ?? "").startsWith("+");
|
|
13
|
+
}
|
|
8
14
|
return {
|
|
9
15
|
name: "houdini",
|
|
10
16
|
enforce: "pre",
|
|
@@ -12,6 +18,11 @@ function document_hmr(ctx) {
|
|
|
12
18
|
async configureServer(server) {
|
|
13
19
|
config = await get_config();
|
|
14
20
|
compiler = await codegen_setup(config, "dev", ctx.db, ctx.db_file);
|
|
21
|
+
generatedDir = path.join(
|
|
22
|
+
server.config.root,
|
|
23
|
+
ctx.config.config_file.runtimeDir ?? ".houdini"
|
|
24
|
+
);
|
|
25
|
+
rootPrefix = server.config.root.endsWith("/") ? server.config.root : `${server.config.root}/`;
|
|
15
26
|
server.httpServer?.once("close", () => {
|
|
16
27
|
compiler.close();
|
|
17
28
|
});
|
|
@@ -32,20 +43,27 @@ function document_hmr(ctx) {
|
|
|
32
43
|
// hotUpdate is called for 'create', 'update', and 'delete' events in Vite 8+.
|
|
33
44
|
// The legacy handleHotUpdate hook is only called for 'update', so new .gql files
|
|
34
45
|
// (type === 'create') would be silently ignored without this hook.
|
|
35
|
-
|
|
46
|
+
// Async so we can read file content eagerly to determine ownership before debouncing.
|
|
47
|
+
async hotUpdate(opts) {
|
|
48
|
+
if (!generatedDir) return;
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
for (const [fp, ts] of ownWrites.entries()) {
|
|
51
|
+
if (now - ts > 1e3) ownWrites.delete(fp);
|
|
52
|
+
}
|
|
36
53
|
const server = opts.server;
|
|
37
|
-
|
|
38
|
-
server.config.root,
|
|
39
|
-
ctx.config.config_file.runtimeDir ?? ".houdini"
|
|
40
|
-
);
|
|
41
|
-
if (opts.file.startsWith(generatedDir + "/") || opts.file === generatedDir) {
|
|
54
|
+
if (opts.file.startsWith(`${generatedDir}/`) || opts.file === generatedDir) {
|
|
42
55
|
return [];
|
|
43
56
|
}
|
|
44
57
|
if (opts.type === "delete") {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
const relPath = opts.file.substring(rootPrefix.length);
|
|
59
|
+
if (!opts.file.endsWith(".gql") && !isManifestFile(opts.file)) {
|
|
60
|
+
const rowCount = ctx.db.get(
|
|
61
|
+
"SELECT COUNT(*) as count FROM raw_documents WHERE filepath = ?",
|
|
62
|
+
[relPath]
|
|
63
|
+
)?.count ?? 0;
|
|
64
|
+
if (rowCount === 0) return;
|
|
65
|
+
}
|
|
66
|
+
debounceHmr.queueDelete(relPath, batchCallback);
|
|
49
67
|
return [];
|
|
50
68
|
}
|
|
51
69
|
const ownWriteTime = ownWrites.get(opts.file);
|
|
@@ -53,162 +71,266 @@ function document_hmr(ctx) {
|
|
|
53
71
|
if (Date.now() - ownWriteTime < 500) return [];
|
|
54
72
|
ownWrites.delete(opts.file);
|
|
55
73
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
let preReadContent = null;
|
|
75
|
+
if (!opts.file.endsWith(".gql")) {
|
|
76
|
+
try {
|
|
77
|
+
preReadContent = await opts.read();
|
|
78
|
+
} catch {
|
|
79
|
+
return;
|
|
61
80
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
if (!preReadContent.includes("$houdini")) {
|
|
82
|
+
if (!isManifestFile(opts.file)) {
|
|
83
|
+
const relPath = opts.file.substring(rootPrefix.length);
|
|
84
|
+
const rowCount = ctx.db.get(
|
|
85
|
+
"SELECT COUNT(*) as count FROM raw_documents WHERE filepath = ?",
|
|
86
|
+
[relPath]
|
|
87
|
+
)?.count ?? 0;
|
|
88
|
+
if (rowCount === 0) return;
|
|
89
|
+
cleanupFiles.add(opts.file);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
debounceHmr.queueUpdate(opts, preReadContent, batchCallback);
|
|
94
|
+
async function batchCallback(files, deletedFiles, task_id) {
|
|
95
|
+
if (!compiler) return;
|
|
96
|
+
await compiler.pipeline_lock(async () => {
|
|
97
|
+
if (deletedFiles.length > 0) {
|
|
98
|
+
const delClause = deletedFiles.map(() => "filepath = ?").join(" OR ");
|
|
99
|
+
ctx.db.run(`DELETE FROM raw_documents WHERE ${delClause}`, deletedFiles);
|
|
100
|
+
}
|
|
101
|
+
const filepaths = [];
|
|
102
|
+
const relativePaths = [];
|
|
103
|
+
const useStdio = config.config_file.pluginTransport === "stdio";
|
|
104
|
+
for (const [filepath, content] of Object.entries(files)) {
|
|
105
|
+
if (!filepath.includes(generatedDir) && (filepath.endsWith(".gql") || content.includes("$houdini") || cleanupFiles.delete(filepath) || isManifestFile(filepath))) {
|
|
106
|
+
const relPath = filepath.substring(rootPrefix.length);
|
|
107
|
+
relativePaths.push(relPath);
|
|
108
|
+
filepaths.push(filepath);
|
|
109
|
+
if (useStdio) {
|
|
110
|
+
let fileContent;
|
|
79
111
|
try {
|
|
80
112
|
fileContent = readFileSync(filepath, "utf-8");
|
|
81
113
|
} catch {
|
|
114
|
+
fileContent = content;
|
|
115
|
+
}
|
|
116
|
+
if (fileContent.trim() !== "") {
|
|
117
|
+
ownWrites.set(filepath, Date.now());
|
|
118
|
+
try {
|
|
119
|
+
writeFileSync(filepath, fileContent, "utf-8");
|
|
120
|
+
} catch {
|
|
121
|
+
ownWrites.delete(filepath);
|
|
122
|
+
}
|
|
82
123
|
}
|
|
83
124
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (filepaths.length === 0) {
|
|
128
|
+
if (deletedFiles.length > 0) {
|
|
129
|
+
console.log(
|
|
130
|
+
`\u{1F3A9} Detected ${deletedFiles.length} deleted ${deletedFiles.length === 1 ? "file" : "files"}, re-running compiler`
|
|
131
|
+
);
|
|
132
|
+
ctx.db.run(`
|
|
133
|
+
UPDATE selections AS s
|
|
134
|
+
SET field_name = s.fragment_ref
|
|
135
|
+
WHERE s.fragment_ref IS NOT NULL
|
|
136
|
+
AND s.kind = 'fragment'
|
|
137
|
+
AND NOT EXISTS (SELECT 1 FROM documents d WHERE d.name = s.field_name)
|
|
138
|
+
`);
|
|
139
|
+
ctx.db.run(`
|
|
140
|
+
WITH orphan_selections AS (
|
|
141
|
+
SELECT s.id FROM selections s
|
|
142
|
+
LEFT JOIN selection_refs rp ON rp.parent_id = s.id
|
|
143
|
+
LEFT JOIN selection_refs rc ON rc.child_id = s.id
|
|
144
|
+
WHERE rp.id IS NULL AND rc.id IS NULL
|
|
145
|
+
)
|
|
146
|
+
DELETE FROM selections WHERE id IN (SELECT id FROM orphan_selections)
|
|
147
|
+
`);
|
|
148
|
+
try {
|
|
149
|
+
const results2 = await run_pipeline(compiler.trigger_hook, {
|
|
150
|
+
after: "AfterExtract"
|
|
151
|
+
});
|
|
152
|
+
const updated_modules2 = [
|
|
153
|
+
...Object.values(results2.GenerateDocuments || {}).flat(),
|
|
154
|
+
...Object.values(results2.GenerateRuntime || {}).flat()
|
|
155
|
+
];
|
|
156
|
+
const seenUrls2 = /* @__PURE__ */ new Set();
|
|
157
|
+
const updates2 = updated_modules2.flatMap((module_path) => {
|
|
158
|
+
const resolvedPath = module_path.startsWith("$houdini/") ? module_path.replace("$houdini", generatedDir) : module_path;
|
|
159
|
+
const mods = [
|
|
160
|
+
...server.moduleGraph.getModulesByFile(resolvedPath) ?? []
|
|
161
|
+
];
|
|
162
|
+
return mods.flatMap((mod) => {
|
|
163
|
+
if (seenUrls2.has(mod.url)) return [];
|
|
164
|
+
seenUrls2.add(mod.url);
|
|
165
|
+
server.moduleGraph.invalidateModule(mod);
|
|
166
|
+
return [
|
|
167
|
+
{
|
|
168
|
+
type: "js-update",
|
|
169
|
+
path: mod.url,
|
|
170
|
+
acceptedPath: mod.url,
|
|
171
|
+
timestamp: Date.now()
|
|
172
|
+
}
|
|
173
|
+
];
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
if (updates2.length > 0) {
|
|
177
|
+
server.ws.send({ type: "update", updates: updates2 });
|
|
90
178
|
}
|
|
179
|
+
} catch (err) {
|
|
180
|
+
console.error("[houdini] pipeline error after deletion:", err);
|
|
91
181
|
}
|
|
92
182
|
}
|
|
183
|
+
return;
|
|
93
184
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
185
|
+
const fileCount = filepaths.length;
|
|
186
|
+
console.log(
|
|
187
|
+
`\u{1F3A9} Detected ${fileCount} file ${fileCount === 1 ? "change" : "changes"}, re-running compiler`
|
|
188
|
+
);
|
|
189
|
+
const eqClause = relativePaths.map(() => "filepath = ?").join(" OR ");
|
|
190
|
+
const savedDocs = ctx.db.all(
|
|
191
|
+
`SELECT id, offset_line, offset_column, filepath, content, current_task, loaded_with
|
|
192
|
+
FROM raw_documents WHERE ${eqClause}`,
|
|
193
|
+
relativePaths
|
|
194
|
+
);
|
|
195
|
+
ctx.db.run(`DELETE from raw_documents WHERE ${eqClause}`, relativePaths);
|
|
196
|
+
ctx.db.run(`
|
|
197
|
+
UPDATE selections AS s
|
|
198
|
+
SET field_name = s.fragment_ref
|
|
199
|
+
WHERE s.fragment_ref IS NOT NULL
|
|
200
|
+
AND s.kind = 'fragment'
|
|
201
|
+
AND NOT EXISTS (SELECT 1 FROM documents d WHERE d.name = s.field_name)
|
|
202
|
+
`);
|
|
203
|
+
ctx.db.run(`
|
|
204
|
+
WITH orphan_selections AS (
|
|
205
|
+
SELECT s.id FROM selections s
|
|
206
|
+
LEFT JOIN selection_refs rp ON rp.parent_id = s.id
|
|
207
|
+
LEFT JOIN selection_refs rc ON rc.child_id = s.id
|
|
208
|
+
WHERE rp.id IS NULL AND rc.id IS NULL
|
|
209
|
+
)
|
|
210
|
+
DELETE FROM selections WHERE id IN (SELECT id FROM orphan_selections)
|
|
211
|
+
`);
|
|
212
|
+
try {
|
|
213
|
+
await compiler.trigger_hook("ExtractDocuments", {
|
|
214
|
+
payload: { filepaths }
|
|
215
|
+
});
|
|
216
|
+
} catch (err) {
|
|
217
|
+
for (const doc of savedDocs) {
|
|
218
|
+
try {
|
|
219
|
+
ctx.db.run(
|
|
220
|
+
`INSERT OR IGNORE INTO raw_documents
|
|
221
|
+
(offset_line, offset_column, filepath, content, loaded_with)
|
|
222
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
223
|
+
[
|
|
224
|
+
doc.offset_line,
|
|
225
|
+
doc.offset_column,
|
|
226
|
+
doc.filepath,
|
|
227
|
+
doc.content,
|
|
228
|
+
doc.loaded_with
|
|
229
|
+
]
|
|
230
|
+
);
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
ctx.db.flush();
|
|
235
|
+
ctx.db.reload();
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
ctx.db.run(`UPDATE raw_documents SET current_task = ? WHERE ${eqClause}`, [
|
|
239
|
+
task_id,
|
|
240
|
+
...relativePaths
|
|
241
|
+
]);
|
|
242
|
+
const changes = ctx.db.rowsModified();
|
|
243
|
+
if (changes === 0 && savedDocs.length === 0) {
|
|
244
|
+
if (!filepaths.some(isManifestFile)) return;
|
|
245
|
+
}
|
|
246
|
+
await compiler.trigger_hook("AfterExtract", { task_id });
|
|
247
|
+
ctx.db.run(
|
|
248
|
+
`
|
|
249
|
+
WITH RECURSIVE
|
|
250
|
+
seed AS (
|
|
251
|
+
SELECT DISTINCT d.name FROM raw_documents rd
|
|
252
|
+
JOIN documents d ON d.raw_document = rd.id
|
|
253
|
+
WHERE rd.current_task = $task_id
|
|
254
|
+
),
|
|
255
|
+
up AS (
|
|
256
|
+
SELECT name FROM seed
|
|
257
|
+
UNION
|
|
258
|
+
SELECT d2.name FROM up u
|
|
259
|
+
JOIN document_dependencies dd ON dd.depends_on = u.name
|
|
260
|
+
JOIN documents d2 ON d2.id = dd.document
|
|
261
|
+
),
|
|
262
|
+
down AS (
|
|
263
|
+
SELECT name FROM seed
|
|
264
|
+
UNION
|
|
265
|
+
SELECT dd.depends_on FROM down v
|
|
266
|
+
JOIN documents d ON d.name = v.name
|
|
267
|
+
JOIN document_dependencies dd ON dd.document = d.id
|
|
268
|
+
),
|
|
269
|
+
targets_up AS (
|
|
270
|
+
SELECT DISTINCT d.raw_document AS raw_id FROM documents d
|
|
271
|
+
JOIN up u ON u.name = d.name WHERE d.raw_document IS NOT NULL
|
|
272
|
+
),
|
|
273
|
+
targets_down AS (
|
|
274
|
+
SELECT DISTINCT d.raw_document AS raw_id FROM documents d
|
|
275
|
+
JOIN down v ON v.name = d.name
|
|
276
|
+
JOIN raw_documents rd ON rd.id = d.raw_document
|
|
277
|
+
WHERE d.raw_document IS NOT NULL
|
|
278
|
+
),
|
|
279
|
+
targets AS (SELECT raw_id FROM targets_up UNION SELECT raw_id FROM targets_down)
|
|
280
|
+
UPDATE raw_documents SET current_task = $task_id
|
|
281
|
+
WHERE id IN (SELECT raw_id FROM targets)
|
|
282
|
+
`,
|
|
283
|
+
{ $task_id: task_id }
|
|
284
|
+
);
|
|
285
|
+
let results;
|
|
286
|
+
let taskDocCount = 0;
|
|
287
|
+
try {
|
|
288
|
+
results = await run_pipeline(compiler.trigger_hook, {
|
|
289
|
+
task_id,
|
|
290
|
+
after: "AfterExtract"
|
|
291
|
+
});
|
|
292
|
+
taskDocCount = ctx.db.get(
|
|
293
|
+
`SELECT COUNT(DISTINCT d.id) as count
|
|
294
|
+
FROM documents d
|
|
295
|
+
JOIN raw_documents rd ON rd.id = d.raw_document
|
|
296
|
+
WHERE rd.current_task = ?`,
|
|
297
|
+
[task_id]
|
|
298
|
+
)?.count ?? 0;
|
|
299
|
+
} finally {
|
|
300
|
+
ctx.db.run(
|
|
301
|
+
`UPDATE raw_documents SET current_task = NULL WHERE current_task = ?`,
|
|
302
|
+
[task_id]
|
|
99
303
|
);
|
|
100
|
-
await compiler.run_pipeline({});
|
|
101
304
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
await compiler.trigger_hook("ExtractDocuments", {
|
|
128
|
-
payload: { filepaths }
|
|
305
|
+
console.log(
|
|
306
|
+
`\u{1F3A9} Updated ${taskDocCount} ${taskDocCount === 1 ? "document" : "documents"}`
|
|
307
|
+
);
|
|
308
|
+
const updated_modules = [
|
|
309
|
+
...Object.values(results.GenerateDocuments || {}).flat(),
|
|
310
|
+
...Object.values(results.GenerateRuntime || {}).flat()
|
|
311
|
+
];
|
|
312
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
313
|
+
const updates = updated_modules.flatMap((module_path) => {
|
|
314
|
+
const resolvedPath = module_path.startsWith("$houdini/") ? module_path.replace("$houdini", generatedDir) : module_path;
|
|
315
|
+
const mods = [...server.moduleGraph.getModulesByFile(resolvedPath) ?? []];
|
|
316
|
+
return mods.flatMap((mod) => {
|
|
317
|
+
if (seenUrls.has(mod.url)) return [];
|
|
318
|
+
seenUrls.add(mod.url);
|
|
319
|
+
server.moduleGraph.invalidateModule(mod);
|
|
320
|
+
return [
|
|
321
|
+
{
|
|
322
|
+
type: "js-update",
|
|
323
|
+
path: mod.url,
|
|
324
|
+
acceptedPath: mod.url,
|
|
325
|
+
timestamp: Date.now()
|
|
326
|
+
}
|
|
327
|
+
];
|
|
328
|
+
});
|
|
129
329
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
throw err;
|
|
133
|
-
}
|
|
134
|
-
ctx.db.run(`UPDATE raw_documents SET current_task = ? WHERE ${eqClause}`, [
|
|
135
|
-
task_id,
|
|
136
|
-
...relativePaths
|
|
137
|
-
]);
|
|
138
|
-
const changes = ctx.db.rowsModified();
|
|
139
|
-
if (changes === 0) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
await compiler.trigger_hook("AfterExtract", { task_id });
|
|
143
|
-
ctx.db.run(
|
|
144
|
-
`
|
|
145
|
-
WITH RECURSIVE
|
|
146
|
-
seed AS (
|
|
147
|
-
SELECT DISTINCT d.name FROM raw_documents rd
|
|
148
|
-
JOIN documents d ON d.raw_document = rd.id
|
|
149
|
-
WHERE rd.current_task = $task_id
|
|
150
|
-
),
|
|
151
|
-
up AS (
|
|
152
|
-
SELECT name FROM seed
|
|
153
|
-
UNION
|
|
154
|
-
SELECT d2.name FROM up u
|
|
155
|
-
JOIN document_dependencies dd ON dd.depends_on = u.name
|
|
156
|
-
JOIN documents d2 ON d2.id = dd.document
|
|
157
|
-
WHERE printed IS NOT NULL
|
|
158
|
-
),
|
|
159
|
-
down AS (
|
|
160
|
-
SELECT name FROM seed
|
|
161
|
-
UNION
|
|
162
|
-
SELECT dd.depends_on FROM down v
|
|
163
|
-
JOIN documents d ON d.name = v.name
|
|
164
|
-
JOIN document_dependencies dd ON dd.document = d.id
|
|
165
|
-
),
|
|
166
|
-
targets_up AS (
|
|
167
|
-
SELECT DISTINCT d.raw_document AS raw_id FROM documents d
|
|
168
|
-
JOIN up u ON u.name = d.name WHERE d.raw_document IS NOT NULL
|
|
169
|
-
),
|
|
170
|
-
targets_down AS (
|
|
171
|
-
SELECT DISTINCT d.raw_document AS raw_id FROM documents d
|
|
172
|
-
JOIN down v ON v.name = d.name
|
|
173
|
-
JOIN raw_documents rd ON rd.id = d.raw_document
|
|
174
|
-
WHERE d.raw_document IS NOT NULL
|
|
175
|
-
),
|
|
176
|
-
targets AS (SELECT raw_id FROM targets_up UNION SELECT raw_id FROM targets_down)
|
|
177
|
-
UPDATE raw_documents SET current_task = $task_id
|
|
178
|
-
WHERE id IN (SELECT raw_id FROM targets)
|
|
179
|
-
`,
|
|
180
|
-
{ $task_id: task_id }
|
|
181
|
-
);
|
|
182
|
-
const results = await run_pipeline(compiler.trigger_hook, {
|
|
183
|
-
task_id,
|
|
184
|
-
after: "AfterExtract"
|
|
185
|
-
});
|
|
186
|
-
const taskDocCount = ctx.db.get(
|
|
187
|
-
`SELECT COUNT(DISTINCT d.id) as count
|
|
188
|
-
FROM documents d
|
|
189
|
-
JOIN raw_documents rd ON rd.id = d.raw_document
|
|
190
|
-
WHERE rd.current_task = ?`,
|
|
191
|
-
[task_id]
|
|
192
|
-
)?.count ?? 0;
|
|
193
|
-
console.log(
|
|
194
|
-
`\u{1F3A9} Updated ${taskDocCount} ${taskDocCount === 1 ? "document" : "documents"}`
|
|
195
|
-
);
|
|
196
|
-
const updated_modules = [
|
|
197
|
-
...Object.values(results.GenerateDocuments || {}).flat(),
|
|
198
|
-
...Object.values(results.GenerateRuntime || {}).flat()
|
|
199
|
-
];
|
|
200
|
-
ctx.db.run(`UPDATE raw_documents SET current_task = NULL WHERE current_task = ?`, [
|
|
201
|
-
task_id
|
|
202
|
-
]);
|
|
203
|
-
for (const module_path of updated_modules) {
|
|
204
|
-
const mod = server.moduleGraph.getModuleById(module_path);
|
|
205
|
-
if (mod) {
|
|
206
|
-
server.moduleGraph.invalidateModule(mod);
|
|
330
|
+
if (updates.length > 0) {
|
|
331
|
+
server.ws.send({ type: "update", updates });
|
|
207
332
|
}
|
|
208
|
-
}
|
|
209
|
-
if (updated_modules.length > 0) {
|
|
210
|
-
server.ws.send({ type: "full-reload" });
|
|
211
|
-
}
|
|
333
|
+
});
|
|
212
334
|
}
|
|
213
335
|
}
|
|
214
336
|
};
|
|
@@ -232,20 +354,30 @@ function createDebounceHmr(debounceMs = 50) {
|
|
|
232
354
|
deleteQueue.clear();
|
|
233
355
|
updateTimer = null;
|
|
234
356
|
if (isProcessing) {
|
|
235
|
-
pendingBatch
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
357
|
+
if (pendingBatch) {
|
|
358
|
+
for (const [fp, entry] of filesToProcess.entries()) {
|
|
359
|
+
pendingBatch.files.set(fp, entry);
|
|
360
|
+
}
|
|
361
|
+
for (const relPath of filesToDelete) {
|
|
362
|
+
pendingBatch.deletedFiles.add(relPath);
|
|
363
|
+
}
|
|
364
|
+
pendingBatch.batchId = currentBatchId;
|
|
365
|
+
} else {
|
|
366
|
+
pendingBatch = {
|
|
367
|
+
files: filesToProcess,
|
|
368
|
+
deletedFiles: filesToDelete,
|
|
369
|
+
batchId: currentBatchId
|
|
370
|
+
};
|
|
371
|
+
}
|
|
240
372
|
return;
|
|
241
373
|
}
|
|
242
374
|
isProcessing = true;
|
|
243
375
|
try {
|
|
244
376
|
const filesWithContent = {};
|
|
245
377
|
await Promise.all(
|
|
246
|
-
Array.from(filesToProcess.entries()).map(async ([filepath,
|
|
378
|
+
Array.from(filesToProcess.entries()).map(async ([filepath, entry]) => {
|
|
247
379
|
try {
|
|
248
|
-
filesWithContent[filepath] = await
|
|
380
|
+
filesWithContent[filepath] = typeof entry === "string" ? entry : await entry();
|
|
249
381
|
} catch {
|
|
250
382
|
}
|
|
251
383
|
})
|
|
@@ -264,9 +396,9 @@ function createDebounceHmr(debounceMs = 50) {
|
|
|
264
396
|
pendingBatch = null;
|
|
265
397
|
const nextFilesWithContent = {};
|
|
266
398
|
await Promise.all(
|
|
267
|
-
Array.from(nextFiles.entries()).map(async ([filepath,
|
|
399
|
+
Array.from(nextFiles.entries()).map(async ([filepath, entry]) => {
|
|
268
400
|
try {
|
|
269
|
-
nextFilesWithContent[filepath] = await
|
|
401
|
+
nextFilesWithContent[filepath] = typeof entry === "string" ? entry : await entry();
|
|
270
402
|
} catch {
|
|
271
403
|
}
|
|
272
404
|
})
|
|
@@ -287,9 +419,9 @@ function createDebounceHmr(debounceMs = 50) {
|
|
|
287
419
|
}, debounceMs);
|
|
288
420
|
}
|
|
289
421
|
return {
|
|
290
|
-
queueUpdate(ctx, callback) {
|
|
422
|
+
queueUpdate(ctx, preReadContent, callback) {
|
|
291
423
|
if (!updateQueue.has(ctx.file)) {
|
|
292
|
-
updateQueue.set(ctx.file, ctx.read);
|
|
424
|
+
updateQueue.set(ctx.file, preReadContent !== null ? preReadContent : ctx.read);
|
|
293
425
|
}
|
|
294
426
|
scheduleFlush(callback);
|
|
295
427
|
},
|
package/build/vite/houdini.js
CHANGED
|
@@ -93,6 +93,12 @@ function houdini(ctx) {
|
|
|
93
93
|
fs: {
|
|
94
94
|
...userConfig.server?.fs,
|
|
95
95
|
allow: ["."].concat(userConfig.server?.fs?.allow || [])
|
|
96
|
+
},
|
|
97
|
+
watch: {
|
|
98
|
+
...userConfig.server?.watch,
|
|
99
|
+
ignored: ["**/*.houdini_tmp"].concat(
|
|
100
|
+
userConfig.server?.watch?.ignored || []
|
|
101
|
+
)
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
};
|
package/build/vite/schema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as graphql from "graphql";
|
|
2
1
|
import path from "node:path";
|
|
3
|
-
import
|
|
2
|
+
import * as graphql from "graphql";
|
|
3
|
+
import { fs, get_config, run_pipeline } from "../lib/index.js";
|
|
4
4
|
import { pull_schema } from "../lib/schema.js";
|
|
5
5
|
import { sleep } from "../lib/sleep.js";
|
|
6
6
|
import { compiler } from "./hmr.js";
|
|
@@ -21,7 +21,9 @@ function refresh_on_schema(_ctx) {
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
try {
|
|
24
|
-
await
|
|
24
|
+
await compiler.pipeline_lock(
|
|
25
|
+
() => run_pipeline(compiler.trigger_hook, { start: "Schema" })
|
|
26
|
+
);
|
|
25
27
|
} catch (e) {
|
|
26
28
|
console.error(e);
|
|
27
29
|
}
|
|
@@ -120,7 +122,9 @@ function watch_local_schema(_ctx) {
|
|
|
120
122
|
setTimeout(() => ownSchemaWrites.delete(write_target), 2e3);
|
|
121
123
|
if (!compiler) return;
|
|
122
124
|
try {
|
|
123
|
-
await
|
|
125
|
+
await compiler.pipeline_lock(
|
|
126
|
+
() => run_pipeline(compiler.trigger_hook, { start: "Schema" })
|
|
127
|
+
);
|
|
124
128
|
} catch (e) {
|
|
125
129
|
console.error(e);
|
|
126
130
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "houdini",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.32",
|
|
4
4
|
"description": "The disappearing GraphQL clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"recast": "^0.23.11",
|
|
51
51
|
"sql.js": "^1.14.1",
|
|
52
52
|
"ws": "^8.21.0",
|
|
53
|
-
"houdini-core": "^2.0.0-next.
|
|
53
|
+
"houdini-core": "^2.0.0-next.20"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"graphql": ">=16",
|