houdini 2.0.3 → 2.0.4
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 -2
- package/build/lib/codegen.js +16 -10
- package/build/lib/config.d.ts +0 -4
- package/build/lib/config.js +0 -3
- package/build/lib/database.d.ts +1 -1
- package/build/lib/database.js +17 -7
- package/build/lib/db.d.ts +1 -0
- package/build/lib/db.js +30 -0
- package/build/lib/error.d.ts +7 -2
- package/build/lib/error.js +12 -0
- package/build/lib/project.d.ts +1 -0
- package/build/lib/project.js +19 -4
- package/build/package.json +1 -1
- package/build/router/manifest.js +4 -3
- package/build/router/server.js +1 -1
- package/build/router/session.d.ts +6 -1
- package/build/router/session.js +41 -18
- package/build/vite/hmr.js +23 -19
- package/build/vite/schema.js +2 -2
- 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.
|
|
475
|
+
houdini: "^2.0.4"
|
|
476
476
|
};
|
|
477
477
|
if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
|
|
478
478
|
packageJSON2.devDependencies = {
|
package/build/lib/codegen.d.ts
CHANGED
|
@@ -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): Promise<[Db, string]>;
|
|
34
|
-
export declare function init_db(config: Config, preserve: boolean): Promise<[Db, string]>;
|
|
33
|
+
export declare function connect_db(config: Config, db_file?: string): Promise<[Db, string]>;
|
|
34
|
+
export declare function init_db(config: Config, preserve: boolean, db_file?: string): Promise<[Db, string]>;
|
|
35
35
|
export type CompilerProxy = {
|
|
36
36
|
close: () => Promise<void>;
|
|
37
37
|
trigger_hook: (name: PipelineHook, opts?: {
|
package/build/lib/codegen.js
CHANGED
|
@@ -52,12 +52,12 @@ process.exit(0);}
|
|
|
52
52
|
import * as conventions from "../router/conventions.js";
|
|
53
53
|
import { create_schema, schema_version, write_config } from "./database.js";
|
|
54
54
|
import { openDb } from "./db.js";
|
|
55
|
-
import { format_hook_error } from "./error.js";
|
|
55
|
+
import { PluginHookError, format_hook_error } from "./error.js";
|
|
56
56
|
import * as fs from "./fs.js";
|
|
57
57
|
import { Logger } from "./logger.js";
|
|
58
58
|
import { LogLevel } from "./types.js";
|
|
59
|
-
async function connect_db(config) {
|
|
60
|
-
const filepath = conventions.db_path(config);
|
|
59
|
+
async function connect_db(config, db_file) {
|
|
60
|
+
const filepath = db_file ?? conventions.db_path(config);
|
|
61
61
|
let db = await openDb(filepath);
|
|
62
62
|
const stamped = db.get("PRAGMA user_version")?.user_version ?? 0;
|
|
63
63
|
const existing = db.get(`SELECT 1 FROM sqlite_master WHERE type = 'table' LIMIT 1`);
|
|
@@ -82,8 +82,8 @@ async function connect_db(config) {
|
|
|
82
82
|
db.flush();
|
|
83
83
|
return [db, filepath];
|
|
84
84
|
}
|
|
85
|
-
async function init_db(config, preserve) {
|
|
86
|
-
|
|
85
|
+
async function init_db(config, preserve, db_file) {
|
|
86
|
+
db_file ??= conventions.db_path(config);
|
|
87
87
|
if (!preserve) {
|
|
88
88
|
try {
|
|
89
89
|
await fs.remove(db_file);
|
|
@@ -98,7 +98,7 @@ async function init_db(config, preserve) {
|
|
|
98
98
|
} catch (_e) {
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
return connect_db(config);
|
|
101
|
+
return connect_db(config, db_file);
|
|
102
102
|
}
|
|
103
103
|
function plugin_db_key(name) {
|
|
104
104
|
if (!name.startsWith("./") && !name.startsWith("../") && !path.isAbsolute(name)) {
|
|
@@ -245,7 +245,7 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
245
245
|
format_hook_error(config.root_dir, error, name, pending.hook);
|
|
246
246
|
});
|
|
247
247
|
pending.reject(
|
|
248
|
-
Object.assign(new
|
|
248
|
+
Object.assign(new PluginHookError(name, pending.hook, errors), {
|
|
249
249
|
alreadyLogged: true
|
|
250
250
|
})
|
|
251
251
|
);
|
|
@@ -326,7 +326,10 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
326
326
|
// stdio/WASM plugins carry the protocol over stdin+stdout, so those must be pipes.
|
|
327
327
|
// websocket plugins talk over TCP, so let stdout/stderr through to the console.
|
|
328
328
|
stdio: pluginUsesStdio ? ["pipe", "pipe", "inherit"] : ["inherit", "inherit", "inherit"],
|
|
329
|
-
detached:
|
|
329
|
+
// stdio plugins can't be detached: WebContainers doesn't plumb the pipe
|
|
330
|
+
// to a detached child's fd 0, so the plugin gets EBADF reading stdin.
|
|
331
|
+
// They're torn down by closing stdin, so they don't need a process group.
|
|
332
|
+
detached: process.platform !== "win32" && !pluginUsesStdio
|
|
330
333
|
});
|
|
331
334
|
if (pluginUsesStdio) {
|
|
332
335
|
stdioStdin.set(dbKey, child.stdin);
|
|
@@ -380,7 +383,7 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
380
383
|
format_hook_error(config.root_dir, error, name, pending.hook);
|
|
381
384
|
});
|
|
382
385
|
pending.reject(
|
|
383
|
-
Object.assign(new
|
|
386
|
+
Object.assign(new PluginHookError(name, pending.hook, errors), {
|
|
384
387
|
alreadyLogged: true
|
|
385
388
|
})
|
|
386
389
|
);
|
|
@@ -545,7 +548,10 @@ async function codegen_setup(config, mode, db, db_file) {
|
|
|
545
548
|
} else {
|
|
546
549
|
try {
|
|
547
550
|
process.kill(plugin.process.pid, 0);
|
|
548
|
-
process.kill(
|
|
551
|
+
process.kill(
|
|
552
|
+
plugin.port === 0 ? plugin.process.pid : -plugin.process.pid,
|
|
553
|
+
"SIGINT"
|
|
554
|
+
);
|
|
549
555
|
} catch {
|
|
550
556
|
}
|
|
551
557
|
}
|
package/build/lib/config.d.ts
CHANGED
|
@@ -3,9 +3,6 @@ import type { GraphQLSchema } from 'graphql';
|
|
|
3
3
|
import type { OAuthProvider, OAuthUser, OAuthTokens } from '../oauth/index.js';
|
|
4
4
|
import type { PluginMeta } from './project.js';
|
|
5
5
|
import type { CachePolicies, PaginateModes } from './types.js';
|
|
6
|
-
declare namespace App {
|
|
7
|
-
type Session = {};
|
|
8
|
-
}
|
|
9
6
|
export type ConfigFile = {
|
|
10
7
|
/**
|
|
11
8
|
* A glob pointing to all files that houdini should consider. Note, this must include .js files
|
|
@@ -249,7 +246,6 @@ export declare class Config {
|
|
|
249
246
|
schema: GraphQLSchema;
|
|
250
247
|
});
|
|
251
248
|
schema_path(): string;
|
|
252
|
-
get localApiDir(): string;
|
|
253
249
|
api_url(): Promise<string | undefined>;
|
|
254
250
|
get include(): Array<string>;
|
|
255
251
|
includeFile(filepath: string, { root }?: {
|
package/build/lib/config.js
CHANGED
|
@@ -22,9 +22,6 @@ class Config {
|
|
|
22
22
|
schema_path() {
|
|
23
23
|
return this.config_file.schemaPath ?? path.resolve(process.cwd(), "schema.json");
|
|
24
24
|
}
|
|
25
|
-
get localApiDir() {
|
|
26
|
-
return path.join(this.root_dir, "src", "api");
|
|
27
|
-
}
|
|
28
25
|
async api_url() {
|
|
29
26
|
const watchSchema = this.config_file.watchSchema;
|
|
30
27
|
if (watchSchema === false || watchSchema === null) {
|
package/build/lib/database.d.ts
CHANGED
|
@@ -2,6 +2,6 @@ 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 providers TEXT\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\n-- refetch_meta carries the \"refetch\" artifact-block metadata for documents that are\n-- refetchable but are NOT lists (e.g. @refetchable fragments). Lists keep their refetch\n-- metadata on discovered_lists since it is intrinsic to pagination; this table is for the\n-- list-less case so we don't have to fake a discovered_lists row.\nCREATE TABLE IF NOT EXISTS refetch_meta (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n document INTEGER NOT NULL,\n selection INTEGER NOT NULL,\n target_type TEXT NOT NULL,\n method TEXT NOT NULL DEFAULT 'offset',\n mode TEXT NOT NULL DEFAULT 'Infinite',\n page_size INTEGER NOT NULL DEFAULT 0,\n embedded BOOLEAN NOT NULL DEFAULT false,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n FOREIGN KEY (selection) REFERENCES selections(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-- refetch_meta\nCREATE INDEX IF NOT EXISTS idx_refetch_meta_selection ON refetch_meta(selection);\nCREATE INDEX IF NOT EXISTS idx_refetch_meta_document ON refetch_meta(document);\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";
|
|
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 providers TEXT\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);\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\t\t-- created by the pipeline rather than written by the user (component field\n\t\t-- fragments, list operations, pagination/refetch queries, argument variants).\n\t\t-- distinct from internal, which controls network-document stripping.\n\t\tgenerated boolean default false,\n -- type_condition is user input and intentionally not a foreign key into\n -- types(name): a fragment on an unknown type must survive extraction so\n -- validation can report it (and a schema change that removes a type must\n -- not cascade-delete the user's documents)\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\n-- directive columns on the user-reference tables (selection_directives,\n-- document_directives, document_variable_directives) are intentionally NOT\n-- foreign keys into directives(name): documents are extracted before list\n-- delete directives (User_delete, ...) are registered by the generation phase,\n-- and unknown names must survive extraction so validateDirectives can report\n-- them as validation errors instead of an opaque constraint failure.\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);\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);\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\n-- refetch_meta carries the \"refetch\" artifact-block metadata for documents that are\n-- refetchable but are NOT lists (e.g. @refetchable fragments). Lists keep their refetch\n-- metadata on discovered_lists since it is intrinsic to pagination; this table is for the\n-- list-less case so we don't have to fake a discovered_lists row.\nCREATE TABLE IF NOT EXISTS refetch_meta (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n document INTEGER NOT NULL,\n selection INTEGER NOT NULL,\n target_type TEXT NOT NULL,\n method TEXT NOT NULL DEFAULT 'offset',\n mode TEXT NOT NULL DEFAULT 'Infinite',\n page_size INTEGER NOT NULL DEFAULT 0,\n embedded BOOLEAN NOT NULL DEFAULT false,\n\n FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE,\n FOREIGN KEY (selection) REFERENCES selections(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-- refetch_meta\nCREATE INDEX IF NOT EXISTS idx_refetch_meta_selection ON refetch_meta(selection);\nCREATE INDEX IF NOT EXISTS idx_refetch_meta_document ON refetch_meta(document);\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 const schema_version: number;
|
|
7
7
|
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
|
@@ -193,8 +193,7 @@ CREATE TABLE IF NOT EXISTS document_variable_directives (
|
|
|
193
193
|
directive TEXT NOT NULL,
|
|
194
194
|
row INTEGER NOT NULL,
|
|
195
195
|
column INTEGER NOT NULL,
|
|
196
|
-
FOREIGN KEY (parent) REFERENCES document_variables(id) ON DELETE CASCADE
|
|
197
|
-
FOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE
|
|
196
|
+
FOREIGN KEY (parent) REFERENCES document_variables(id) ON DELETE CASCADE
|
|
198
197
|
);
|
|
199
198
|
|
|
200
199
|
CREATE TABLE IF NOT EXISTS document_variable_directive_arguments (
|
|
@@ -238,7 +237,14 @@ CREATE TABLE IF NOT EXISTS documents (
|
|
|
238
237
|
internal boolean default false,
|
|
239
238
|
visible boolean default true,
|
|
240
239
|
processed boolean default false,
|
|
241
|
-
|
|
240
|
+
-- created by the pipeline rather than written by the user (component field
|
|
241
|
+
-- fragments, list operations, pagination/refetch queries, argument variants).
|
|
242
|
+
-- distinct from internal, which controls network-document stripping.
|
|
243
|
+
generated boolean default false,
|
|
244
|
+
-- type_condition is user input and intentionally not a foreign key into
|
|
245
|
+
-- types(name): a fragment on an unknown type must survive extraction so
|
|
246
|
+
-- validation can report it (and a schema change that removes a type must
|
|
247
|
+
-- not cascade-delete the user's documents)
|
|
242
248
|
FOREIGN KEY (raw_document) REFERENCES raw_documents(id) ON DELETE CASCADE
|
|
243
249
|
);
|
|
244
250
|
|
|
@@ -252,14 +258,19 @@ CREATE TABLE IF NOT EXISTS selections (
|
|
|
252
258
|
fragment_args JSON -- used to store the arguments that are used when fragment variables are expanded
|
|
253
259
|
);
|
|
254
260
|
|
|
261
|
+
-- directive columns on the user-reference tables (selection_directives,
|
|
262
|
+
-- document_directives, document_variable_directives) are intentionally NOT
|
|
263
|
+
-- foreign keys into directives(name): documents are extracted before list
|
|
264
|
+
-- delete directives (User_delete, ...) are registered by the generation phase,
|
|
265
|
+
-- and unknown names must survive extraction so validateDirectives can report
|
|
266
|
+
-- them as validation errors instead of an opaque constraint failure.
|
|
255
267
|
CREATE TABLE IF NOT EXISTS selection_directives (
|
|
256
268
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
257
269
|
selection_id INTEGER NOT NULL,
|
|
258
270
|
directive TEXT NOT NULL,
|
|
259
271
|
row INTEGER NOT NULL,
|
|
260
272
|
column INTEGER NOT NULL,
|
|
261
|
-
FOREIGN KEY (selection_id) REFERENCES selections(id) ON DELETE CASCADE
|
|
262
|
-
FOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE
|
|
273
|
+
FOREIGN KEY (selection_id) REFERENCES selections(id) ON DELETE CASCADE
|
|
263
274
|
);
|
|
264
275
|
|
|
265
276
|
CREATE TABLE IF NOT EXISTS selection_directive_arguments (
|
|
@@ -280,8 +291,7 @@ CREATE TABLE IF NOT EXISTS document_directives (
|
|
|
280
291
|
directive TEXT NOT NULL,
|
|
281
292
|
row INTEGER NOT NULL,
|
|
282
293
|
column INTEGER NOT NULL,
|
|
283
|
-
FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE
|
|
284
|
-
FOREIGN KEY (directive) REFERENCES directives(name) ON DELETE CASCADE
|
|
294
|
+
FOREIGN KEY (document) REFERENCES documents(id) ON DELETE CASCADE
|
|
285
295
|
);
|
|
286
296
|
|
|
287
297
|
CREATE TABLE IF NOT EXISTS document_directive_arguments (
|
package/build/lib/db.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface Db {
|
|
|
3
3
|
readonly filepath: string;
|
|
4
4
|
exec(sql: string): void;
|
|
5
5
|
run(sql: string, params?: Params): void;
|
|
6
|
+
transaction<T>(fn: () => T): T;
|
|
6
7
|
get<T extends Record<string, any> = Record<string, any>>(sql: string, params?: Params): T | undefined;
|
|
7
8
|
all<T extends Record<string, any> = Record<string, any>>(sql: string, params?: Params): T[];
|
|
8
9
|
rowsModified(): number;
|
package/build/lib/db.js
CHANGED
|
@@ -18,6 +18,21 @@ class SqlJsDb {
|
|
|
18
18
|
this._db.run(sql, params);
|
|
19
19
|
this._lastRowsModified = this._db.getRowsModified();
|
|
20
20
|
}
|
|
21
|
+
transaction(fn) {
|
|
22
|
+
this._db.run("BEGIN");
|
|
23
|
+
this._db.run("PRAGMA defer_foreign_keys = ON");
|
|
24
|
+
try {
|
|
25
|
+
const result = fn();
|
|
26
|
+
this._db.run("COMMIT");
|
|
27
|
+
return result;
|
|
28
|
+
} catch (err) {
|
|
29
|
+
try {
|
|
30
|
+
this._db.run("ROLLBACK");
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
21
36
|
get(sql, params) {
|
|
22
37
|
const stmt = this._db.prepare(sql);
|
|
23
38
|
if (params !== void 0) stmt.bind(params);
|
|
@@ -70,6 +85,21 @@ class NativeDb {
|
|
|
70
85
|
const result = params === void 0 ? stmt.run() : Array.isArray(params) ? stmt.run(...params) : stmt.run(this._norm(params));
|
|
71
86
|
this._lastChanges = result?.changes ?? 0;
|
|
72
87
|
}
|
|
88
|
+
transaction(fn) {
|
|
89
|
+
this._conn.exec("BEGIN");
|
|
90
|
+
this._conn.exec("PRAGMA defer_foreign_keys = ON");
|
|
91
|
+
try {
|
|
92
|
+
const result = fn();
|
|
93
|
+
this._conn.exec("COMMIT");
|
|
94
|
+
return result;
|
|
95
|
+
} catch (err) {
|
|
96
|
+
try {
|
|
97
|
+
this._conn.exec("ROLLBACK");
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
73
103
|
get(sql, params) {
|
|
74
104
|
const stmt = this._conn.prepare(sql);
|
|
75
105
|
return params === void 0 ? stmt.get() : Array.isArray(params) ? stmt.get(...params) : stmt.get(this._norm(params));
|
package/build/lib/error.d.ts
CHANGED
|
@@ -14,12 +14,17 @@ export type HookError = {
|
|
|
14
14
|
locations: HookErrorLocation[];
|
|
15
15
|
kind: string;
|
|
16
16
|
};
|
|
17
|
-
type HookErrorLocation = {
|
|
17
|
+
export type HookErrorLocation = {
|
|
18
18
|
filepath: string;
|
|
19
19
|
line: number;
|
|
20
20
|
column: number;
|
|
21
21
|
};
|
|
22
|
+
export declare class PluginHookError extends Error {
|
|
23
|
+
plugin: string;
|
|
24
|
+
hook: string;
|
|
25
|
+
errors: HookError[];
|
|
26
|
+
constructor(plugin: string, hook: string, errors: HookError[]);
|
|
27
|
+
}
|
|
22
28
|
export declare function format_hook_error(rootDir: string, error: HookError, plugin: string, hook: string): void;
|
|
23
29
|
export declare function format_codeblock(code: string[], lineNrStart: number): string;
|
|
24
30
|
export declare function formatErrors(e: unknown, afterError?: (e: Error) => void): void;
|
|
25
|
-
export {};
|
package/build/lib/error.js
CHANGED
|
@@ -36,6 +36,17 @@ function format_error(e, after) {
|
|
|
36
36
|
after?.(e);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
class PluginHookError extends Error {
|
|
40
|
+
constructor(plugin, hook, errors) {
|
|
41
|
+
super(`Failed to call ${plugin}`);
|
|
42
|
+
this.plugin = plugin;
|
|
43
|
+
this.hook = hook;
|
|
44
|
+
this.errors = errors;
|
|
45
|
+
}
|
|
46
|
+
plugin;
|
|
47
|
+
hook;
|
|
48
|
+
errors;
|
|
49
|
+
}
|
|
39
50
|
function format_hook_error(rootDir, error, plugin, hook) {
|
|
40
51
|
let message = `-- ${styleText(
|
|
41
52
|
"red",
|
|
@@ -108,6 +119,7 @@ function formatErrors(e, afterError) {
|
|
|
108
119
|
}
|
|
109
120
|
export {
|
|
110
121
|
HoudiniError,
|
|
122
|
+
PluginHookError,
|
|
111
123
|
formatErrors,
|
|
112
124
|
format_codeblock,
|
|
113
125
|
format_error,
|
package/build/lib/project.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare function get_config({ force_reload, config_path: _config_path, sk
|
|
|
14
14
|
skip_schema?: boolean;
|
|
15
15
|
}): Promise<Config>;
|
|
16
16
|
export declare function load_vite_env(configPath: string): Promise<Record<string, string>>;
|
|
17
|
+
export declare function load_env_files(rootDir: string): Promise<void>;
|
|
17
18
|
export declare function read_config_file(configPath: string): Promise<ConfigFile>;
|
|
18
19
|
export declare function internal_routes(config: Config): string[];
|
|
19
20
|
export declare function load_local_schema(config: ConfigFile, schema_path: string): Promise<graphql.GraphQLSchema>;
|
package/build/lib/project.js
CHANGED
|
@@ -78,7 +78,10 @@ async function get_config({
|
|
|
78
78
|
}
|
|
79
79
|
} catch {
|
|
80
80
|
}
|
|
81
|
-
const server_config = await read_server_config(
|
|
81
|
+
const server_config = await read_server_config(
|
|
82
|
+
local_server_dir(_config, root_dir),
|
|
83
|
+
root_dir
|
|
84
|
+
);
|
|
82
85
|
const partialConfig = {
|
|
83
86
|
root_dir,
|
|
84
87
|
config_file,
|
|
@@ -134,8 +137,7 @@ function parse_env_file(contents) {
|
|
|
134
137
|
}
|
|
135
138
|
return out;
|
|
136
139
|
}
|
|
137
|
-
async function
|
|
138
|
-
const root = path.dirname(configPath);
|
|
140
|
+
async function collect_env_files(root) {
|
|
139
141
|
const mode = process.env.NODE_ENV || "development";
|
|
140
142
|
const collected = {};
|
|
141
143
|
for (const file of [".env", ".env.local", `.env.${mode}`, `.env.${mode}.local`]) {
|
|
@@ -144,6 +146,10 @@ async function load_vite_env(configPath) {
|
|
|
144
146
|
Object.assign(collected, parse_env_file(contents));
|
|
145
147
|
}
|
|
146
148
|
}
|
|
149
|
+
return collected;
|
|
150
|
+
}
|
|
151
|
+
async function load_vite_env(configPath) {
|
|
152
|
+
const collected = await collect_env_files(path.dirname(configPath));
|
|
147
153
|
for (const [key, value] of Object.entries(process.env)) {
|
|
148
154
|
if (value !== void 0 && key.startsWith(ENV_PREFIX)) {
|
|
149
155
|
collected[key] = value;
|
|
@@ -157,6 +163,13 @@ async function load_vite_env(configPath) {
|
|
|
157
163
|
}
|
|
158
164
|
return exposed;
|
|
159
165
|
}
|
|
166
|
+
async function load_env_files(rootDir) {
|
|
167
|
+
for (const [key, value] of Object.entries(await collect_env_files(rootDir))) {
|
|
168
|
+
if (process.env[key] === void 0) {
|
|
169
|
+
process.env[key] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
160
173
|
async function read_config_file(configPath) {
|
|
161
174
|
let imported;
|
|
162
175
|
const source = await fs.readFile(configPath);
|
|
@@ -209,7 +222,7 @@ ${e.message}`);
|
|
|
209
222
|
...config
|
|
210
223
|
};
|
|
211
224
|
}
|
|
212
|
-
async function read_server_config(serverDir) {
|
|
225
|
+
async function read_server_config(serverDir, rootDir) {
|
|
213
226
|
let configPath = "";
|
|
214
227
|
try {
|
|
215
228
|
for (const child of await fs.readdir(serverDir)) {
|
|
@@ -224,6 +237,7 @@ async function read_server_config(serverDir) {
|
|
|
224
237
|
if (!configPath) {
|
|
225
238
|
return {};
|
|
226
239
|
}
|
|
240
|
+
await load_env_files(rootDir);
|
|
227
241
|
let imported;
|
|
228
242
|
try {
|
|
229
243
|
imported = await import(
|
|
@@ -290,6 +304,7 @@ export {
|
|
|
290
304
|
default_config,
|
|
291
305
|
get_config,
|
|
292
306
|
internal_routes,
|
|
307
|
+
load_env_files,
|
|
293
308
|
load_local_schema,
|
|
294
309
|
load_vite_env,
|
|
295
310
|
read_config_file
|
package/build/package.json
CHANGED
package/build/router/manifest.js
CHANGED
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
read_layoutView,
|
|
10
10
|
read_pageView,
|
|
11
11
|
read_pageQuery,
|
|
12
|
-
page_id
|
|
12
|
+
page_id,
|
|
13
|
+
local_server_dir
|
|
13
14
|
} from "./conventions.js";
|
|
14
15
|
import { parse_page_pattern } from "./match.js";
|
|
15
16
|
async function load_manifest(args) {
|
|
@@ -43,8 +44,8 @@ async function load_manifest(args) {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
try {
|
|
46
|
-
await fs.stat(args.config
|
|
47
|
-
for (const child of await fs.readdir(args.config
|
|
47
|
+
await fs.stat(local_server_dir(args.config));
|
|
48
|
+
for (const child of await fs.readdir(local_server_dir(args.config), {
|
|
48
49
|
withFileTypes: true
|
|
49
50
|
})) {
|
|
50
51
|
const name = child.isDirectory() ? child.name : path.parse(child.name).name;
|
package/build/router/server.js
CHANGED
|
@@ -304,7 +304,7 @@ function _serverHandler({
|
|
|
304
304
|
const value = valueAtPath(result.data, sessionWrite.sessionPath.split("."));
|
|
305
305
|
const req = { request, config: config_file, session_keys };
|
|
306
306
|
if (value == null) {
|
|
307
|
-
clear_session(formResponse);
|
|
307
|
+
clear_session(formResponse, request);
|
|
308
308
|
} else if (sessionWrite.merge) {
|
|
309
309
|
const existing = await get_session(request.headers, session_keys);
|
|
310
310
|
await set_session(req, formResponse, { ...existing, ...value });
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ConfigFile, ServerConfigFile } from '../lib/index.js';
|
|
2
|
+
export declare function insecure_loopback(request: {
|
|
3
|
+
url?: string;
|
|
4
|
+
}): boolean;
|
|
2
5
|
export declare function isAllowedOrigin(request: Request, server_config?: ServerConfigFile): boolean;
|
|
3
6
|
export type ServerHandlerArgs = {
|
|
4
7
|
request: Request;
|
|
@@ -21,7 +24,9 @@ export type ServerResponse = {
|
|
|
21
24
|
set_header(name: string, value: string): void;
|
|
22
25
|
};
|
|
23
26
|
export declare const session_cookie_name = "__houdini__";
|
|
24
|
-
export declare function clear_session(response: Response
|
|
27
|
+
export declare function clear_session(response: Response, request?: {
|
|
28
|
+
url?: string;
|
|
29
|
+
}): void;
|
|
25
30
|
export declare function sign_session(session: App.Session, key: string): Promise<string>;
|
|
26
31
|
export declare function set_session(req: ServerHandlerArgs, response: Response, value: App.Session): Promise<void>;
|
|
27
32
|
export declare function get_session(req: Headers, secrets: string[]): Promise<App.Session>;
|
package/build/router/session.js
CHANGED
|
@@ -11,12 +11,21 @@ import {
|
|
|
11
11
|
} from "./auth-token.js";
|
|
12
12
|
import { parse } from "./cookies.js";
|
|
13
13
|
import { decode, encode, verify } from "./jwt.js";
|
|
14
|
+
function insecure_loopback(request) {
|
|
15
|
+
try {
|
|
16
|
+
const { protocol, hostname } = new URL(request.url ?? "");
|
|
17
|
+
return protocol === "http:" && (hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1");
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
14
22
|
const REDIRECT_TXN_COOKIE = "__Host-houdini-txn";
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
const REDIRECT_TXN_COOKIE_INSECURE = "houdini-txn";
|
|
24
|
+
function redirectTxnCookie(value, secure) {
|
|
25
|
+
return secure ? `${REDIRECT_TXN_COOKIE}=${value}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${REDIRECT_TXN_TTL_SECONDS}` : `${REDIRECT_TXN_COOKIE_INSECURE}=${value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${REDIRECT_TXN_TTL_SECONDS}`;
|
|
17
26
|
}
|
|
18
|
-
function clearRedirectTxnCookie() {
|
|
19
|
-
return `${REDIRECT_TXN_COOKIE}=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0`;
|
|
27
|
+
function clearRedirectTxnCookie(secure) {
|
|
28
|
+
return secure ? `${REDIRECT_TXN_COOKIE}=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0` : `${REDIRECT_TXN_COOKIE_INSECURE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`;
|
|
20
29
|
}
|
|
21
30
|
function isAllowedOrigin(request, server_config) {
|
|
22
31
|
const origin = request.headers.get("origin");
|
|
@@ -52,7 +61,7 @@ async function applySessionToken(args, response, token) {
|
|
|
52
61
|
return false;
|
|
53
62
|
}
|
|
54
63
|
if ("clear" in verified) {
|
|
55
|
-
clear_session(response);
|
|
64
|
+
clear_session(response, args.request);
|
|
56
65
|
} else if (verified.merge) {
|
|
57
66
|
await set_session(args, response, { ...presenter, ...verified.session });
|
|
58
67
|
} else {
|
|
@@ -120,7 +129,10 @@ async function login_endpoint(args) {
|
|
|
120
129
|
status: 302,
|
|
121
130
|
headers: { Location: target.toString() }
|
|
122
131
|
});
|
|
123
|
-
response.headers.append(
|
|
132
|
+
response.headers.append(
|
|
133
|
+
"Set-Cookie",
|
|
134
|
+
redirectTxnCookie(txn, !insecure_loopback(args.request))
|
|
135
|
+
);
|
|
124
136
|
return response;
|
|
125
137
|
}
|
|
126
138
|
return new Response("Unknown provider", { status: 400 });
|
|
@@ -154,13 +166,13 @@ async function oauth_start(args, provider, providerName, redirectTo) {
|
|
|
154
166
|
status: 302,
|
|
155
167
|
headers: { Location: authorizeUrl.toString() }
|
|
156
168
|
});
|
|
157
|
-
response.headers.append("Set-Cookie", redirectTxnCookie(txn));
|
|
169
|
+
response.headers.append("Set-Cookie", redirectTxnCookie(txn, !insecure_loopback(args.request)));
|
|
158
170
|
return response;
|
|
159
171
|
}
|
|
160
172
|
async function oauth_callback(args, txn, searchParams) {
|
|
161
173
|
const deny = () => new Response("Forbidden", {
|
|
162
174
|
status: 403,
|
|
163
|
-
headers: { "Set-Cookie": clearRedirectTxnCookie() }
|
|
175
|
+
headers: { "Set-Cookie": clearRedirectTxnCookie(!insecure_loopback(args.request)) }
|
|
164
176
|
});
|
|
165
177
|
const provider = args.server_config.auth.providers[txn.provider];
|
|
166
178
|
const as = await provider.server();
|
|
@@ -185,7 +197,8 @@ async function oauth_callback(args, txn, searchParams) {
|
|
|
185
197
|
// resolveEndpoint honors it), but oauth4webapi omits it from this options type, so cast.
|
|
186
198
|
[oauth.allowInsecureRequests]: Boolean(provider.allowInsecureRequests)
|
|
187
199
|
});
|
|
188
|
-
} catch {
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error(`[houdini] oauth token exchange with "${txn.provider}" failed:`, e);
|
|
189
202
|
return deny();
|
|
190
203
|
}
|
|
191
204
|
const tokens = {
|
|
@@ -201,7 +214,7 @@ async function oauth_callback(args, txn, searchParams) {
|
|
|
201
214
|
const session = onSignIn ? await onSignIn({ provider: txn.provider, user, tokens }) : {};
|
|
202
215
|
const response = new Response("ok", { status: 302, headers: { Location: txn.redirectTo } });
|
|
203
216
|
await set_session(args, response, session ?? {});
|
|
204
|
-
response.headers.append("Set-Cookie", clearRedirectTxnCookie());
|
|
217
|
+
response.headers.append("Set-Cookie", clearRedirectTxnCookie(!insecure_loopback(args.request)));
|
|
205
218
|
return response;
|
|
206
219
|
}
|
|
207
220
|
async function auth_endpoint(args) {
|
|
@@ -215,12 +228,16 @@ async function auth_endpoint(args) {
|
|
|
215
228
|
`http://${args.request.headers.get("host")}`
|
|
216
229
|
);
|
|
217
230
|
const cookies = parse(args.request.headers.get("cookie") ?? "");
|
|
218
|
-
const
|
|
231
|
+
const rawTxn = cookies[REDIRECT_TXN_COOKIE] ?? (insecure_loopback(args.request) ? cookies[REDIRECT_TXN_COOKIE_INSECURE] : void 0);
|
|
232
|
+
const txn = await verifyRedirectTxn(rawTxn, args.session_keys);
|
|
219
233
|
const state = searchParams.get("state");
|
|
220
234
|
if (!txn || !state || !timingSafeEqual(txn.nonce, state)) {
|
|
235
|
+
console.error(
|
|
236
|
+
`[houdini] redirect-login callback rejected: ` + (!rawTxn ? `no transaction cookie on the request (was the flow started at /login?)` : !txn ? `transaction cookie failed verification (expired, or sessionKeys changed mid-flight)` : !state ? `no state parameter on the callback` : `state does not match the transaction nonce`)
|
|
237
|
+
);
|
|
221
238
|
return new Response("Forbidden", {
|
|
222
239
|
status: 403,
|
|
223
|
-
headers: { "Set-Cookie": clearRedirectTxnCookie() }
|
|
240
|
+
headers: { "Set-Cookie": clearRedirectTxnCookie(!insecure_loopback(args.request)) }
|
|
224
241
|
});
|
|
225
242
|
}
|
|
226
243
|
if (txn.provider && auth.providers?.[txn.provider]) {
|
|
@@ -229,7 +246,10 @@ async function auth_endpoint(args) {
|
|
|
229
246
|
const response = new Response("ok", { status: 302, headers: { Location: txn.redirectTo } });
|
|
230
247
|
const applied = await applySessionToken(args, response, searchParams.get("token"));
|
|
231
248
|
const result = applied ? response : new Response("Forbidden", { status: 403 });
|
|
232
|
-
result.headers.append(
|
|
249
|
+
result.headers.append(
|
|
250
|
+
"Set-Cookie",
|
|
251
|
+
clearRedirectTxnCookie(!insecure_loopback(args.request))
|
|
252
|
+
);
|
|
233
253
|
return result;
|
|
234
254
|
}
|
|
235
255
|
if (args.request.method === "POST") {
|
|
@@ -246,7 +266,7 @@ async function auth_endpoint(args) {
|
|
|
246
266
|
status: 303,
|
|
247
267
|
headers: { Location: safeRelative(formData.get("redirectTo")) }
|
|
248
268
|
});
|
|
249
|
-
clear_session(response2);
|
|
269
|
+
clear_session(response2, args.request);
|
|
250
270
|
return response2;
|
|
251
271
|
}
|
|
252
272
|
const body = await args.request.json().catch(() => null);
|
|
@@ -261,7 +281,7 @@ async function auth_endpoint(args) {
|
|
|
261
281
|
return response;
|
|
262
282
|
}
|
|
263
283
|
if (body.session === null) {
|
|
264
|
-
clear_session(response);
|
|
284
|
+
clear_session(response, args.request);
|
|
265
285
|
return response;
|
|
266
286
|
}
|
|
267
287
|
const existing = await get_session(args.request.headers, args.session_keys);
|
|
@@ -270,10 +290,11 @@ async function auth_endpoint(args) {
|
|
|
270
290
|
}
|
|
271
291
|
}
|
|
272
292
|
const session_cookie_name = "__houdini__";
|
|
273
|
-
function clear_session(response) {
|
|
293
|
+
function clear_session(response, request) {
|
|
294
|
+
const secure = request && insecure_loopback(request) ? "" : "Secure; ";
|
|
274
295
|
response.headers.set(
|
|
275
296
|
"Set-Cookie",
|
|
276
|
-
`${session_cookie_name}=; Path=/; HttpOnly;
|
|
297
|
+
`${session_cookie_name}=; Path=/; HttpOnly; ${secure}SameSite=Lax; Max-Age=0`
|
|
277
298
|
);
|
|
278
299
|
}
|
|
279
300
|
function safeRelative(value) {
|
|
@@ -292,9 +313,10 @@ async function set_session(req, response, value) {
|
|
|
292
313
|
{ ...value, exp: Math.floor(expires.getTime() / 1e3) },
|
|
293
314
|
req.session_keys[0]
|
|
294
315
|
);
|
|
316
|
+
const secure = insecure_loopback(req.request) ? "" : "Secure; ";
|
|
295
317
|
response.headers.set(
|
|
296
318
|
"Set-Cookie",
|
|
297
|
-
`${session_cookie_name}=${serialized}; Path=/; HttpOnly;
|
|
319
|
+
`${session_cookie_name}=${serialized}; Path=/; HttpOnly; ${secure}SameSite=Lax; Expires=${expires.toUTCString()} `
|
|
298
320
|
);
|
|
299
321
|
}
|
|
300
322
|
async function get_session(req, secrets) {
|
|
@@ -331,6 +353,7 @@ export {
|
|
|
331
353
|
clear_session,
|
|
332
354
|
get_session,
|
|
333
355
|
handle_request,
|
|
356
|
+
insecure_loopback,
|
|
334
357
|
isAllowedOrigin,
|
|
335
358
|
session_cookie_name,
|
|
336
359
|
set_session,
|
package/build/vite/hmr.js
CHANGED
|
@@ -177,22 +177,24 @@ function document_hmr(ctx) {
|
|
|
177
177
|
console.log(
|
|
178
178
|
`\u{1F3A9} Detected ${deletedFiles.length} deleted ${deletedFiles.length === 1 ? "file" : "files"}, re-running compiler`
|
|
179
179
|
);
|
|
180
|
-
ctx.db.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
180
|
+
ctx.db.transaction(() => {
|
|
181
|
+
ctx.db.run(`
|
|
182
|
+
UPDATE selections AS s
|
|
183
|
+
SET field_name = s.fragment_ref
|
|
184
|
+
WHERE s.fragment_ref IS NOT NULL
|
|
185
|
+
AND s.kind = 'fragment'
|
|
186
|
+
AND NOT EXISTS (SELECT 1 FROM documents d WHERE d.name = s.field_name)
|
|
187
|
+
`);
|
|
188
|
+
ctx.db.run(`
|
|
189
|
+
WITH orphan_selections AS (
|
|
190
|
+
SELECT s.id FROM selections s
|
|
191
|
+
LEFT JOIN selection_refs rp ON rp.parent_id = s.id
|
|
192
|
+
LEFT JOIN selection_refs rc ON rc.child_id = s.id
|
|
193
|
+
WHERE rp.id IS NULL AND rc.id IS NULL
|
|
194
|
+
)
|
|
195
|
+
DELETE FROM selections WHERE id IN (SELECT id FROM orphan_selections)
|
|
196
|
+
`);
|
|
197
|
+
});
|
|
196
198
|
try {
|
|
197
199
|
const results2 = await run_pipeline(compiler.trigger_hook, {
|
|
198
200
|
after: "AfterExtract"
|
|
@@ -218,15 +220,16 @@ function document_hmr(ctx) {
|
|
|
218
220
|
FROM raw_documents WHERE ${eqClause}`,
|
|
219
221
|
relativePaths
|
|
220
222
|
);
|
|
221
|
-
ctx.db.
|
|
222
|
-
|
|
223
|
+
ctx.db.transaction(() => {
|
|
224
|
+
ctx.db.run(`DELETE from raw_documents WHERE ${eqClause}`, relativePaths);
|
|
225
|
+
ctx.db.run(`
|
|
223
226
|
UPDATE selections AS s
|
|
224
227
|
SET field_name = s.fragment_ref
|
|
225
228
|
WHERE s.fragment_ref IS NOT NULL
|
|
226
229
|
AND s.kind = 'fragment'
|
|
227
230
|
AND NOT EXISTS (SELECT 1 FROM documents d WHERE d.name = s.field_name)
|
|
228
231
|
`);
|
|
229
|
-
|
|
232
|
+
ctx.db.run(`
|
|
230
233
|
WITH orphan_selections AS (
|
|
231
234
|
SELECT s.id FROM selections s
|
|
232
235
|
LEFT JOIN selection_refs rp ON rp.parent_id = s.id
|
|
@@ -235,6 +238,7 @@ function document_hmr(ctx) {
|
|
|
235
238
|
)
|
|
236
239
|
DELETE FROM selections WHERE id IN (SELECT id FROM orphan_selections)
|
|
237
240
|
`);
|
|
241
|
+
});
|
|
238
242
|
try {
|
|
239
243
|
await compiler.trigger_hook("ExtractDocuments", {
|
|
240
244
|
payload: { filepaths }
|
package/build/vite/schema.js
CHANGED
|
@@ -103,7 +103,7 @@ function watch_local_schema(_ctx) {
|
|
|
103
103
|
if (opts.type === "delete") return;
|
|
104
104
|
const config = await get_config();
|
|
105
105
|
const local_schema_path = await resolve_local_schema(
|
|
106
|
-
path.join(config.root_dir, "src", "
|
|
106
|
+
path.join(config.root_dir, "src", "server", "+schema")
|
|
107
107
|
);
|
|
108
108
|
if (!local_schema_path) return;
|
|
109
109
|
const schema_mod_path = `${local_schema_path}?t=${Date.now()}`;
|
|
@@ -143,7 +143,7 @@ async function resolve_local_schema(base) {
|
|
|
143
143
|
}
|
|
144
144
|
async function write_local_schema(server, root_dir, schema_path) {
|
|
145
145
|
const local_schema_path = await resolve_local_schema(
|
|
146
|
-
path.join(root_dir, "src", "
|
|
146
|
+
path.join(root_dir, "src", "server", "+schema")
|
|
147
147
|
);
|
|
148
148
|
if (!local_schema_path) return;
|
|
149
149
|
let schema;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "houdini",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "The disappearing GraphQL clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"recast": "^0.23.11",
|
|
53
53
|
"sql.js": "^1.14.1",
|
|
54
54
|
"ws": "^8.21.0",
|
|
55
|
-
"houdini-core": "^2.0.
|
|
55
|
+
"houdini-core": "^2.0.3"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"graphql": ">=16",
|