@fougere/adapter-duckdb 0.19.0-alpha.0 → 0.20.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,25 +1,14 @@
1
1
  import { type Entity } from '@fougere/schema';
2
- /** The engines DuckDB can attach — and the reason the list is short. */
3
- export type Attachable = 'sqlite' | 'postgres' | 'mysql';
4
- export interface SourceDeclaration {
5
- /** A file, for sqlite. */
2
+ /** An entry of `sources:` as this reader sees it — what its own adapter declared, nothing added. */
3
+ export interface SourceEntry {
4
+ type?: string;
6
5
  path?: string;
7
- /** A connection string, for an engine reached over the network. */
8
- attach?: string;
9
- /** Defaults to sqlite, the engine `db:` resolves from a name. */
10
- type?: Attachable;
11
- /** Registration names of the entities whose rows live here. */
12
- entities: string[];
6
+ url?: string;
7
+ entities?: string[];
13
8
  }
14
9
  export interface ConnectOptions {
15
- /** The default source — where an entity no declaration names lives. */
16
- db: {
17
- path?: string;
18
- attach?: string;
19
- type?: Attachable;
20
- };
21
- /** The other places, exactly as `fougere.config.ts` states them. */
22
- sources?: Record<string, SourceDeclaration>;
10
+ /** The app's sources, exactly as `fougere.config.ts` states them. */
11
+ sources: Record<string, SourceEntry>;
23
12
  /** What this scope may read — and therefore what gets attached at all. */
24
13
  reads: readonly Entity[];
25
14
  /** Same resolver the storage uses, when an app renames tables. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,MAAM,EAAmB,MAAM,iBAAiB,CAAC;AAI3E,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,EAAE,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,UAAU,CAAA;KAAE,CAAC;IAC1D,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC5C,0EAA0E;IAC1E,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;AAED,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,6DAA6D;IAC7D,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChH,uEAAuE;IACvE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA+BD,kDAAkD;AAClD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,CA4D5E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,MAAM,EAAmB,MAAM,iBAAiB,CAAC;AAI3E,oGAAoG;AACpG,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,0EAA0E;IAC1E,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;AAED,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,6DAA6D;IAC7D,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChH,uEAAuE;IACvE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAeD,kDAAkD;AAClD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,CAkE5E"}
package/dist/index.js CHANGED
@@ -2,34 +2,27 @@
2
2
  import { DuckDBInstance } from '@duckdb/node-api';
3
3
  import { lowerFirst } from '@fougere/schema';
4
4
  import { toTable, toTableName, toSnakeCase, codecsOf } from '@fougere/adapter-sql';
5
- const EXTENSION = {
6
- sqlite: 'sqlite',
7
- postgres: 'postgres',
8
- mysql: 'mysql',
5
+ /**
6
+ * What DuckDB attaches, read off the entry's own type — the extension it loads and the key that
7
+ * says where. MSSQL, files and memory have no extension, which is why the list is short.
8
+ */
9
+ const ATTACHABLE = {
10
+ sqlite: { extension: 'sqlite', target: (entry) => entry.path },
11
+ pg: { extension: 'postgres', target: (entry) => entry.url },
12
+ mysql: { extension: 'mysql', target: (entry) => entry.url },
9
13
  };
10
- const TYPE_CLAUSE = {
11
- sqlite: 'sqlite',
12
- postgres: 'postgres',
13
- mysql: 'mysql',
14
- };
15
- /** The default source's alias — the same word `fougere.config.ts` uses for it. */
16
- const DEFAULT_ALIAS = 'db';
17
- /** 'Book' and 'book' name the same rows — the spelling `sources:` accepts either way. */
18
- function targetOf(declaration, alias) {
19
- const target = declaration.attach ?? declaration.path;
20
- if (!target) {
21
- throw new Error(`source '${alias}': neither \`path\` nor \`attach\` — DuckDB is told what to open, ` +
22
- `and an in-memory database of its own would hold none of your rows.`);
23
- }
24
- return target;
25
- }
14
+ /** DuckDB reserves `main`, `temp` and `system`, and a source may be called any of them. */
15
+ const attachedAs = (source) => `source_${source}`;
26
16
  /** Open a read-only view of the app's sources. */
27
17
  export async function connectSources(options) {
28
18
  const resolve = options.tableName ?? toTableName;
29
- // Where each named entity lives; anything unnamed is in the default source.
19
+ // Where each named entity lives; anything unnamed is in the one source listing none.
30
20
  const home = new Map();
31
- for (const [alias, declaration] of Object.entries(options.sources ?? {})) {
32
- for (const entity of declaration.entities)
21
+ let fallback;
22
+ for (const [alias, entry] of Object.entries(options.sources)) {
23
+ if (entry.entities === undefined)
24
+ fallback = alias;
25
+ for (const entity of entry.entities ?? [])
33
26
  home.set(lowerFirst(entity), alias);
34
27
  }
35
28
  // Only what this scope reads is attached — the declaration IS the environment.
@@ -37,7 +30,9 @@ export async function connectSources(options) {
37
30
  const placed = new Map();
38
31
  for (const shape of options.reads) {
39
32
  const name = lowerFirst(shape.name ?? '');
40
- const alias = home.get(name) ?? DEFAULT_ALIAS;
33
+ const alias = home.get(name) ?? fallback;
34
+ if (!alias)
35
+ throw new Error(`${name} lives in no declared source — no source lists it, and none holds the rest.`);
41
36
  wanted.add(alias);
42
37
  placed.set(shape, { alias, table: resolve(name) });
43
38
  }
@@ -45,20 +40,23 @@ export async function connectSources(options) {
45
40
  const db = await instance.connect();
46
41
  const attached = new Map();
47
42
  for (const alias of wanted) {
48
- const declaration = alias === DEFAULT_ALIAS
49
- ? { ...options.db, entities: [] }
50
- : options.sources?.[alias];
51
- if (!declaration)
43
+ const entry = options.sources[alias];
44
+ if (!entry)
52
45
  throw new Error(`source '${alias}' is read but never declared.`);
53
- const type = declaration.type ?? 'sqlite';
54
- if (!(type in EXTENSION)) {
55
- throw new Error(`source '${alias}' is a ${type} database, which DuckDB cannot attach — there is no ` +
46
+ const type = entry.type ?? 'sqlite';
47
+ const attachable = ATTACHABLE[type];
48
+ if (!attachable) {
49
+ throw new Error(`source '${alias}' is a ${type} source, which DuckDB cannot attach — there is no ` +
56
50
  `such extension. Read it by key through its own storage, or mirror it into one of ` +
57
- `${Object.keys(EXTENSION).join(', ')}.`);
51
+ `${Object.keys(ATTACHABLE).join(', ')}.`);
52
+ }
53
+ const target = attachable.target(entry);
54
+ if (!target) {
55
+ throw new Error(`source '${alias}': nothing says where it is — DuckDB is told what to open.`);
58
56
  }
59
- await db.run(`INSTALL ${EXTENSION[type]}; LOAD ${EXTENSION[type]};`);
60
- await db.run(`ATTACH '${targetOf(declaration, alias)}' AS ${quote(alias)} (TYPE ${TYPE_CLAUSE[type]}, READ_ONLY);`);
61
- attached.set(alias, targetOf(declaration, alias));
57
+ await db.run(`INSTALL ${attachable.extension}; LOAD ${attachable.extension};`);
58
+ await db.run(`ATTACH '${target}' AS ${quote(attachedAs(alias))} (TYPE ${attachable.extension}, READ_ONLY);`);
59
+ attached.set(alias, target);
62
60
  }
63
61
  return {
64
62
  attached,
@@ -82,7 +80,7 @@ export async function connectSources(options) {
82
80
  function qualify(ref, placed) {
83
81
  const found = placed.get(ref);
84
82
  if (found)
85
- return `${quote(found.alias)}.${quote(found.table)}`;
83
+ return `${quote(attachedAs(found.alias))}.${quote(found.table)}`;
86
84
  const name = ref?.name;
87
85
  if (name) {
88
86
  throw new Error(`${name} is named in a query but not in \`reads\` — its source is not attached, so its ` +
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/adapter-duckdb",
3
- "version": "0.19.0-alpha.0",
3
+ "version": "0.20.0-alpha.0",
4
4
  "description": "Read across an app's sources — one SQL query over what DuckDB can attach.",
5
5
  "keywords": [
6
6
  "fougere",
@@ -29,8 +29,8 @@
29
29
  "src"
30
30
  ],
31
31
  "dependencies": {
32
- "@fougere/schema": "0.19.0-alpha.0",
33
- "@fougere/adapter-sql": "0.19.0-alpha.0"
32
+ "@fougere/schema": "0.20.0-alpha.0",
33
+ "@fougere/adapter-sql": "0.20.0-alpha.0"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@duckdb/node-api": ">=1.4.0-0"
package/src/index.ts CHANGED
@@ -4,25 +4,17 @@ import { lowerFirst, type Entity, type SchemaView } from '@fougere/schema';
4
4
  import { toTable, toTableName, toSnakeCase, codecsOf } from '@fougere/adapter-sql';
5
5
 
6
6
 
7
- /** The engines DuckDB can attach — and the reason the list is short. */
8
- export type Attachable = 'sqlite' | 'postgres' | 'mysql';
9
-
10
- export interface SourceDeclaration {
11
- /** A file, for sqlite. */
7
+ /** An entry of `sources:` as this reader sees it — what its own adapter declared, nothing added. */
8
+ export interface SourceEntry {
9
+ type?: string;
12
10
  path?: string;
13
- /** A connection string, for an engine reached over the network. */
14
- attach?: string;
15
- /** Defaults to sqlite, the engine `db:` resolves from a name. */
16
- type?: Attachable;
17
- /** Registration names of the entities whose rows live here. */
18
- entities: string[];
11
+ url?: string;
12
+ entities?: string[];
19
13
  }
20
14
 
21
15
  export interface ConnectOptions {
22
- /** The default source — where an entity no declaration names lives. */
23
- db: { path?: string; attach?: string; type?: Attachable };
24
- /** The other places, exactly as `fougere.config.ts` states them. */
25
- sources?: Record<string, SourceDeclaration>;
16
+ /** The app's sources, exactly as `fougere.config.ts` states them. */
17
+ sources: Record<string, SourceEntry>;
26
18
  /** What this scope may read — and therefore what gets attached at all. */
27
19
  reads: readonly Entity[];
28
20
  /** Same resolver the storage uses, when an app renames tables. */
@@ -38,43 +30,29 @@ export interface Reads {
38
30
  close(): Promise<void>;
39
31
  }
40
32
 
41
- const EXTENSION: Record<Attachable, string> = {
42
- sqlite: 'sqlite',
43
- postgres: 'postgres',
44
- mysql: 'mysql',
45
- };
46
-
47
- const TYPE_CLAUSE: Record<Attachable, string> = {
48
- sqlite: 'sqlite',
49
- postgres: 'postgres',
50
- mysql: 'mysql',
33
+ /**
34
+ * What DuckDB attaches, read off the entry's own type — the extension it loads and the key that
35
+ * says where. MSSQL, files and memory have no extension, which is why the list is short.
36
+ */
37
+ const ATTACHABLE: Record<string, { extension: string; target: (entry: SourceEntry) => string | undefined }> = {
38
+ sqlite: { extension: 'sqlite', target: (entry) => entry.path },
39
+ pg: { extension: 'postgres', target: (entry) => entry.url },
40
+ mysql: { extension: 'mysql', target: (entry) => entry.url },
51
41
  };
52
42
 
53
- /** The default source's alias — the same word `fougere.config.ts` uses for it. */
54
- const DEFAULT_ALIAS = 'db';
55
-
56
- /** 'Book' and 'book' name the same rows — the spelling `sources:` accepts either way. */
57
-
58
- function targetOf(declaration: { path?: string; attach?: string }, alias: string): string {
59
- const target = declaration.attach ?? declaration.path;
60
- if (!target) {
61
- throw new Error(
62
- `source '${alias}': neither \`path\` nor \`attach\` — DuckDB is told what to open, ` +
63
- `and an in-memory database of its own would hold none of your rows.`,
64
- );
65
- }
66
-
67
- return target;
68
- }
43
+ /** DuckDB reserves `main`, `temp` and `system`, and a source may be called any of them. */
44
+ const attachedAs = (source: string): string => `source_${source}`;
69
45
 
70
46
  /** Open a read-only view of the app's sources. */
71
47
  export async function connectSources(options: ConnectOptions): Promise<Reads> {
72
48
  const resolve = options.tableName ?? toTableName;
73
49
 
74
- // Where each named entity lives; anything unnamed is in the default source.
50
+ // Where each named entity lives; anything unnamed is in the one source listing none.
75
51
  const home = new Map<string, string>();
76
- for (const [alias, declaration] of Object.entries(options.sources ?? {})) {
77
- for (const entity of declaration.entities) home.set(lowerFirst(entity), alias);
52
+ let fallback: string | undefined;
53
+ for (const [alias, entry] of Object.entries(options.sources)) {
54
+ if (entry.entities === undefined) fallback = alias;
55
+ for (const entity of entry.entities ?? []) home.set(lowerFirst(entity), alias);
78
56
  }
79
57
 
80
58
  // Only what this scope reads is attached — the declaration IS the environment.
@@ -82,7 +60,8 @@ export async function connectSources(options: ConnectOptions): Promise<Reads> {
82
60
  const placed = new Map<Entity, { alias: string; table: string }>();
83
61
  for (const shape of options.reads) {
84
62
  const name = lowerFirst((shape as { name?: string }).name ?? '');
85
- const alias = home.get(name) ?? DEFAULT_ALIAS;
63
+ const alias = home.get(name) ?? fallback;
64
+ if (!alias) throw new Error(`${name} lives in no declared source — no source lists it, and none holds the rest.`);
86
65
  wanted.add(alias);
87
66
  placed.set(shape, { alias, table: resolve(name) });
88
67
  }
@@ -92,22 +71,25 @@ export async function connectSources(options: ConnectOptions): Promise<Reads> {
92
71
  const attached = new Map<string, string>();
93
72
 
94
73
  for (const alias of wanted) {
95
- const declaration = alias === DEFAULT_ALIAS
96
- ? { ...options.db, entities: [] }
97
- : options.sources?.[alias];
98
- if (!declaration) throw new Error(`source '${alias}' is read but never declared.`);
74
+ const entry = options.sources[alias];
75
+ if (!entry) throw new Error(`source '${alias}' is read but never declared.`);
99
76
 
100
- const type = declaration.type ?? 'sqlite';
101
- if (!(type in EXTENSION)) {
77
+ const type = entry.type ?? 'sqlite';
78
+ const attachable = ATTACHABLE[type];
79
+ if (!attachable) {
102
80
  throw new Error(
103
- `source '${alias}' is a ${type} database, which DuckDB cannot attach — there is no ` +
81
+ `source '${alias}' is a ${type} source, which DuckDB cannot attach — there is no ` +
104
82
  `such extension. Read it by key through its own storage, or mirror it into one of ` +
105
- `${Object.keys(EXTENSION).join(', ')}.`,
83
+ `${Object.keys(ATTACHABLE).join(', ')}.`,
106
84
  );
107
85
  }
108
- await db.run(`INSTALL ${EXTENSION[type]}; LOAD ${EXTENSION[type]};`);
109
- await db.run(`ATTACH '${targetOf(declaration, alias)}' AS ${quote(alias)} (TYPE ${TYPE_CLAUSE[type]}, READ_ONLY);`);
110
- attached.set(alias, targetOf(declaration, alias));
86
+ const target = attachable.target(entry);
87
+ if (!target) {
88
+ throw new Error(`source '${alias}': nothing says where it is — DuckDB is told what to open.`);
89
+ }
90
+ await db.run(`INSTALL ${attachable.extension}; LOAD ${attachable.extension};`);
91
+ await db.run(`ATTACH '${target}' AS ${quote(attachedAs(alias))} (TYPE ${attachable.extension}, READ_ONLY);`);
92
+ attached.set(alias, target);
111
93
  }
112
94
 
113
95
  return {
@@ -133,7 +115,7 @@ export async function connectSources(options: ConnectOptions): Promise<Reads> {
133
115
  /** `${Book}` becomes `archive.books` — the alias AND the table, from one declaration. */
134
116
  function qualify(ref: unknown, placed: Map<Entity, { alias: string; table: string }>): string {
135
117
  const found = placed.get(ref as Entity);
136
- if (found) return `${quote(found.alias)}.${quote(found.table)}`;
118
+ if (found) return `${quote(attachedAs(found.alias))}.${quote(found.table)}`;
137
119
  const name = (ref as { name?: string } | undefined)?.name;
138
120
  if (name) {
139
121
  throw new Error(