@sqlrooms/cells 0.29.0-rc.1

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.
Files changed (99) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +32 -0
  3. package/dist/cellsSlice.d.ts +3 -0
  4. package/dist/cellsSlice.d.ts.map +1 -0
  5. package/dist/cellsSlice.js +602 -0
  6. package/dist/cellsSlice.js.map +1 -0
  7. package/dist/components/DeleteSheetModal.d.ts +9 -0
  8. package/dist/components/DeleteSheetModal.d.ts.map +1 -0
  9. package/dist/components/DeleteSheetModal.js +6 -0
  10. package/dist/components/DeleteSheetModal.js.map +1 -0
  11. package/dist/components/Input/ConfigPanel/DropdownConfig.d.ts +2 -0
  12. package/dist/components/Input/ConfigPanel/DropdownConfig.d.ts.map +1 -0
  13. package/dist/components/Input/ConfigPanel/DropdownConfig.js +44 -0
  14. package/dist/components/Input/ConfigPanel/DropdownConfig.js.map +1 -0
  15. package/dist/components/Input/ConfigPanel/InputConfigPanel.d.ts +11 -0
  16. package/dist/components/Input/ConfigPanel/InputConfigPanel.d.ts.map +1 -0
  17. package/dist/components/Input/ConfigPanel/InputConfigPanel.js +65 -0
  18. package/dist/components/Input/ConfigPanel/InputConfigPanel.js.map +1 -0
  19. package/dist/components/Input/ConfigPanel/SliderConfig.d.ts +2 -0
  20. package/dist/components/Input/ConfigPanel/SliderConfig.d.ts.map +1 -0
  21. package/dist/components/Input/ConfigPanel/SliderConfig.js +19 -0
  22. package/dist/components/Input/ConfigPanel/SliderConfig.js.map +1 -0
  23. package/dist/components/Input/ConfigPanel/TextConfig.d.ts +2 -0
  24. package/dist/components/Input/ConfigPanel/TextConfig.d.ts.map +1 -0
  25. package/dist/components/Input/ConfigPanel/TextConfig.js +8 -0
  26. package/dist/components/Input/ConfigPanel/TextConfig.js.map +1 -0
  27. package/dist/components/Input/helpers.d.ts +3 -0
  28. package/dist/components/Input/helpers.d.ts.map +1 -0
  29. package/dist/components/Input/helpers.js +28 -0
  30. package/dist/components/Input/helpers.js.map +1 -0
  31. package/dist/components/InputCellContent.d.ts +9 -0
  32. package/dist/components/InputCellContent.d.ts.map +1 -0
  33. package/dist/components/InputCellContent.js +38 -0
  34. package/dist/components/InputCellContent.js.map +1 -0
  35. package/dist/components/SheetsTabBar.d.ts +6 -0
  36. package/dist/components/SheetsTabBar.d.ts.map +1 -0
  37. package/dist/components/SheetsTabBar.js +57 -0
  38. package/dist/components/SheetsTabBar.js.map +1 -0
  39. package/dist/components/SqlCellContent.d.ts +9 -0
  40. package/dist/components/SqlCellContent.d.ts.map +1 -0
  41. package/dist/components/SqlCellContent.js +222 -0
  42. package/dist/components/SqlCellContent.js.map +1 -0
  43. package/dist/components/SqlCellRunButton.d.ts +17 -0
  44. package/dist/components/SqlCellRunButton.d.ts.map +1 -0
  45. package/dist/components/SqlCellRunButton.js +11 -0
  46. package/dist/components/SqlCellRunButton.js.map +1 -0
  47. package/dist/components/TextCellContent.d.ts +9 -0
  48. package/dist/components/TextCellContent.d.ts.map +1 -0
  49. package/dist/components/TextCellContent.js +27 -0
  50. package/dist/components/TextCellContent.js.map +1 -0
  51. package/dist/components/VegaCellContent.d.ts +9 -0
  52. package/dist/components/VegaCellContent.d.ts.map +1 -0
  53. package/dist/components/VegaCellContent.js +66 -0
  54. package/dist/components/VegaCellContent.js.map +1 -0
  55. package/dist/components/VegaConfigPanel.d.ts +8 -0
  56. package/dist/components/VegaConfigPanel.d.ts.map +1 -0
  57. package/dist/components/VegaConfigPanel.js +72 -0
  58. package/dist/components/VegaConfigPanel.js.map +1 -0
  59. package/dist/dagUtils.d.ts +20 -0
  60. package/dist/dagUtils.d.ts.map +1 -0
  61. package/dist/dagUtils.js +233 -0
  62. package/dist/dagUtils.js.map +1 -0
  63. package/dist/defaultCellRegistry.d.ts +3 -0
  64. package/dist/defaultCellRegistry.d.ts.map +1 -0
  65. package/dist/defaultCellRegistry.js +177 -0
  66. package/dist/defaultCellRegistry.js.map +1 -0
  67. package/dist/execution.d.ts +9 -0
  68. package/dist/execution.d.ts.map +1 -0
  69. package/dist/execution.js +195 -0
  70. package/dist/execution.js.map +1 -0
  71. package/dist/helpers.d.ts +45 -0
  72. package/dist/helpers.d.ts.map +1 -0
  73. package/dist/helpers.js +124 -0
  74. package/dist/helpers.js.map +1 -0
  75. package/dist/hooks.d.ts +11 -0
  76. package/dist/hooks.d.ts.map +1 -0
  77. package/dist/hooks.js +9 -0
  78. package/dist/hooks.js.map +1 -0
  79. package/dist/index.d.ts +27 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +26 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/resultRelationPolicy.d.ts +74 -0
  84. package/dist/resultRelationPolicy.d.ts.map +1 -0
  85. package/dist/resultRelationPolicy.js +74 -0
  86. package/dist/resultRelationPolicy.js.map +1 -0
  87. package/dist/sqlHelpers.d.ts +41 -0
  88. package/dist/sqlHelpers.d.ts.map +1 -0
  89. package/dist/sqlHelpers.js +190 -0
  90. package/dist/sqlHelpers.js.map +1 -0
  91. package/dist/types.d.ts +501 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +144 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/utils.d.ts +17 -0
  96. package/dist/utils.d.ts.map +1 -0
  97. package/dist/utils.js +35 -0
  98. package/dist/utils.js.map +1 -0
  99. package/package.json +57 -0
@@ -0,0 +1,195 @@
1
+ import { escapeId, makeQualifiedTableName } from '@sqlrooms/duckdb';
2
+ import { convertToValidColumnOrTableName } from '@sqlrooms/utils';
3
+ import { produce } from 'immer';
4
+ import { findSheetIdForCell, getUnqualifiedSqlIdentifier, resolveSheetSchemaName, } from './helpers';
5
+ import { findSqlDependencies, qualifySheetLocalResultNames, renderSqlWithInputs, } from './sqlHelpers';
6
+ import { chooseAdaptiveRelationType, createOrReplaceResultRelation, } from './resultRelationPolicy';
7
+ import { isInputCell, isSqlCell, } from './types';
8
+ import { getEffectiveResultName, isDefined } from './utils';
9
+ const DEFAULT_PAGE_SIZE = 10;
10
+ export async function executeSqlCell(cellId, getState, set, options) {
11
+ // Intentionally snapshot state/cell at trigger time so this run is stable.
12
+ // Later store mutations do not alter this execution; re-read with getState()
13
+ // explicitly if you need live data after awaits.
14
+ const state = getState();
15
+ const cell = state.cells.config.data[cellId];
16
+ if (!cell || cell.type !== 'sql')
17
+ return;
18
+ const previousStatus = state.cells.status[cellId]?.type === 'sql'
19
+ ? state.cells.status[cellId]
20
+ : undefined;
21
+ const { schemaName, cascade = true, signal, setCellResult } = options;
22
+ const sqlRaw = cell.data.sql || '';
23
+ const sheetId = findSheetIdForCell(state, cellId);
24
+ const sheet = sheetId ? state.cells.config.sheets[sheetId] : undefined;
25
+ const finalSchemaName = sheet ? resolveSheetSchemaName(sheet) : schemaName;
26
+ const finalDatabaseName = state.db.config.coreMaterialization.strategy === 'attached_ephemeral'
27
+ ? state.db.config.coreMaterialization.attachedDatabaseName
28
+ : undefined;
29
+ const sheetCellIds = sheet?.cellIds ?? [];
30
+ // 1. Gather inputs for SQL rendering
31
+ const cellsInScope = (sheetCellIds.length ? sheetCellIds : Object.keys(state.cells.config.data))
32
+ .map((id) => state.cells.config.data[id])
33
+ .filter(isDefined);
34
+ const inputs = cellsInScope
35
+ .filter((c) => isInputCell(c))
36
+ .map((c) => ({
37
+ varName: c.data.input.varName,
38
+ value: c.data.input.value,
39
+ }));
40
+ const renderedSql = renderSqlWithInputs(sqlRaw, inputs);
41
+ const sql = qualifySheetLocalResultNames({
42
+ sql: renderedSql,
43
+ sheetSchema: finalSchemaName,
44
+ sheetDatabase: finalDatabaseName,
45
+ sheetCellIds,
46
+ cells: state.cells.config.data,
47
+ getSqlResultName: (id) => {
48
+ const target = state.cells.config.data[id];
49
+ if (!target || target.type !== 'sql')
50
+ return undefined;
51
+ return getEffectiveResultName(target.data, convertToValidColumnOrTableName);
52
+ },
53
+ });
54
+ // 2. Update status to running
55
+ set((s) => produce(s, (draft) => {
56
+ const previousStatus = draft.cells.status[cellId];
57
+ draft.cells.status[cellId] = {
58
+ type: 'sql',
59
+ status: 'running',
60
+ // Keep previous result metadata so the table remains mounted while
61
+ // a fresh query is running.
62
+ resultName: previousStatus?.resultName,
63
+ resultView: previousStatus?.resultView,
64
+ resultRelationType: previousStatus?.resultRelationType,
65
+ lastRunTime: previousStatus?.lastRunTime,
66
+ referencedTables: previousStatus?.referencedTables || [],
67
+ };
68
+ }));
69
+ try {
70
+ const db = state.db;
71
+ const dbConnectors = state.db.connectors;
72
+ if (signal?.aborted)
73
+ throw new Error('Query cancelled');
74
+ const effectiveResultName = getEffectiveResultName(cell.data, convertToValidColumnOrTableName);
75
+ const selectedConnectorId = cell.data.connectorId;
76
+ let tableName = '';
77
+ let relationType = 'view';
78
+ const connector = await db.getConnector();
79
+ if (dbConnectors?.runQuery) {
80
+ const routed = await dbConnectors.runQuery({
81
+ connectionId: selectedConnectorId,
82
+ sql,
83
+ queryType: 'arrow',
84
+ materialize: true,
85
+ materializedName: effectiveResultName,
86
+ materializedSchema: finalSchemaName,
87
+ materializedDatabase: finalDatabaseName,
88
+ signal,
89
+ });
90
+ if (!routed.relationName) {
91
+ throw new Error('Query did not return a materialized relation');
92
+ }
93
+ tableName = routed.relationName;
94
+ relationType = 'table';
95
+ }
96
+ else {
97
+ const parsed = await db.sqlSelectToJson(sql);
98
+ if (parsed.error) {
99
+ throw new Error(parsed.error_message || 'Not a valid SELECT statement');
100
+ }
101
+ if (signal?.aborted)
102
+ throw new Error('Query cancelled');
103
+ await connector.query(`CREATE SCHEMA IF NOT EXISTS ${escapeId(finalSchemaName)}`, { signal });
104
+ tableName = makeQualifiedTableName({
105
+ table: effectiveResultName,
106
+ schema: finalSchemaName,
107
+ database: db.currentDatabase,
108
+ }).toString();
109
+ // Adaptive policy:
110
+ // - first run: choose table when there are downstream dependents,
111
+ // otherwise keep a lightweight view.
112
+ // - subsequent runs: preserve the previously selected relation type.
113
+ const hasDownstream = Boolean(sheetId) &&
114
+ getState().cells.getDownstream(sheetId, cellId).length > 0;
115
+ relationType = chooseAdaptiveRelationType({
116
+ previousRelationType: previousStatus?.resultRelationType,
117
+ hasDownstream,
118
+ });
119
+ if (signal?.aborted)
120
+ throw new Error('Query cancelled');
121
+ await createOrReplaceResultRelation({
122
+ connector,
123
+ relationName: tableName,
124
+ relationType,
125
+ sql,
126
+ signal,
127
+ });
128
+ }
129
+ // Find dependencies for referenced tables
130
+ const referenced = findSqlDependencies({
131
+ targetCell: cell,
132
+ cells: Object.fromEntries(cellsInScope.map((c) => [c.id, c])),
133
+ getSqlText: (c) => (isSqlCell(c) ? c.data.sql : undefined),
134
+ getInputVarName: (c) => isInputCell(c) ? c.data.input.varName : undefined,
135
+ getSqlResultName: (id) => {
136
+ const s = getState().cells.status[id];
137
+ if (s?.type !== 'sql')
138
+ return undefined;
139
+ const fromStatus = getUnqualifiedSqlIdentifier(s.resultName);
140
+ if (fromStatus)
141
+ return fromStatus;
142
+ const currentCell = getState().cells.config.data[id];
143
+ if (currentCell?.type !== 'sql')
144
+ return undefined;
145
+ return getEffectiveResultName(currentCell.data, convertToValidColumnOrTableName);
146
+ },
147
+ });
148
+ // 3. Update status to success
149
+ set((s) => produce(s, (draft) => {
150
+ draft.cells.status[cellId] = {
151
+ type: 'sql',
152
+ status: 'success',
153
+ resultName: tableName,
154
+ resultView: tableName,
155
+ resultRelationType: relationType,
156
+ referencedTables: referenced,
157
+ lastRunTime: Date.now(),
158
+ };
159
+ }));
160
+ // 4. Fetch count + first page and store result
161
+ if (setCellResult) {
162
+ if (signal?.aborted)
163
+ throw new Error('Query cancelled');
164
+ const countResult = await connector.query(`SELECT COUNT(*)::int AS count FROM ${tableName}`, { signal });
165
+ const totalRows = countResult.toArray()[0]?.count ?? 0;
166
+ if (signal?.aborted)
167
+ throw new Error('Query cancelled');
168
+ const pageResult = await connector.query(`SELECT * FROM ${tableName} LIMIT ${DEFAULT_PAGE_SIZE}`, { signal });
169
+ setCellResult(cellId, { arrowTable: pageResult, totalRows });
170
+ }
171
+ // 5. Cascade if needed
172
+ if (cascade) {
173
+ const ownerSheetId = findSheetIdForCell(getState(), cellId);
174
+ if (ownerSheetId) {
175
+ await state.cells.runDownstreamCascade(ownerSheetId, cellId);
176
+ }
177
+ }
178
+ }
179
+ catch (e) {
180
+ const message = e instanceof Error ? e.message : String(e);
181
+ set((s) => produce(s, (draft) => {
182
+ const status = draft.cells.status[cellId];
183
+ if (status?.type === 'sql') {
184
+ status.status = signal?.aborted ? 'cancel' : 'error';
185
+ status.lastError = message;
186
+ }
187
+ }));
188
+ }
189
+ finally {
190
+ set((s) => produce(s, (draft) => {
191
+ delete draft.cells.activeAbortControllers[cellId];
192
+ }));
193
+ }
194
+ }
195
+ //# sourceMappingURL=execution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution.js","sourceRoot":"","sources":["../src/execution.ts"],"names":[],"mappings":"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","sourcesContent":["import {escapeId, makeQualifiedTableName} from '@sqlrooms/duckdb';\nimport {convertToValidColumnOrTableName} from '@sqlrooms/utils';\nimport {produce} from 'immer';\nimport {\n findSheetIdForCell,\n getUnqualifiedSqlIdentifier,\n resolveSheetSchemaName,\n} from './helpers';\nimport {\n findSqlDependencies,\n qualifySheetLocalResultNames,\n renderSqlWithInputs,\n} from './sqlHelpers';\nimport {\n chooseAdaptiveRelationType,\n createOrReplaceResultRelation,\n type ResultRelationType,\n} from './resultRelationPolicy';\nimport {\n isInputCell,\n isSqlCell,\n type CellResultData,\n type CellsRootState,\n type SqlCellData,\n type SqlCellStatus,\n} from './types';\nimport {getEffectiveResultName, isDefined} from './utils';\n\nconst DEFAULT_PAGE_SIZE = 10;\n\nexport type ExecuteSqlCellOptions = {\n schemaName: string;\n cascade?: boolean;\n signal?: AbortSignal;\n setCellResult?: (id: string, data: CellResultData) => void;\n};\n\nexport async function executeSqlCell(\n cellId: string,\n getState: () => CellsRootState,\n set: (updater: (state: CellsRootState) => CellsRootState) => void,\n options: ExecuteSqlCellOptions,\n) {\n // Intentionally snapshot state/cell at trigger time so this run is stable.\n // Later store mutations do not alter this execution; re-read with getState()\n // explicitly if you need live data after awaits.\n const state = getState();\n const cell = state.cells.config.data[cellId];\n if (!cell || cell.type !== 'sql') return;\n const previousStatus =\n state.cells.status[cellId]?.type === 'sql'\n ? state.cells.status[cellId]\n : undefined;\n\n const {schemaName, cascade = true, signal, setCellResult} = options;\n const sqlRaw = cell.data.sql || '';\n const sheetId = findSheetIdForCell(state, cellId);\n const sheet = sheetId ? state.cells.config.sheets[sheetId] : undefined;\n const finalSchemaName = sheet ? resolveSheetSchemaName(sheet) : schemaName;\n const finalDatabaseName =\n state.db.config.coreMaterialization.strategy === 'attached_ephemeral'\n ? state.db.config.coreMaterialization.attachedDatabaseName\n : undefined;\n const sheetCellIds = sheet?.cellIds ?? [];\n\n // 1. Gather inputs for SQL rendering\n const cellsInScope = (\n sheetCellIds.length ? sheetCellIds : Object.keys(state.cells.config.data)\n )\n .map((id) => state.cells.config.data[id])\n .filter(isDefined);\n const inputs = cellsInScope\n .filter((c) => isInputCell(c))\n .map((c) => ({\n varName: c.data.input.varName,\n value: c.data.input.value,\n }));\n\n const renderedSql = renderSqlWithInputs(sqlRaw, inputs);\n const sql = qualifySheetLocalResultNames({\n sql: renderedSql,\n sheetSchema: finalSchemaName,\n sheetDatabase: finalDatabaseName,\n sheetCellIds,\n cells: state.cells.config.data,\n getSqlResultName: (id) => {\n const target = state.cells.config.data[id];\n if (!target || target.type !== 'sql') return undefined;\n return getEffectiveResultName(\n target.data as SqlCellData,\n convertToValidColumnOrTableName,\n );\n },\n });\n\n // 2. Update status to running\n set((s) =>\n produce(s, (draft) => {\n const previousStatus = draft.cells.status[cellId] as\n | SqlCellStatus\n | undefined;\n draft.cells.status[cellId] = {\n type: 'sql',\n status: 'running',\n // Keep previous result metadata so the table remains mounted while\n // a fresh query is running.\n resultName: previousStatus?.resultName,\n resultView: previousStatus?.resultView,\n resultRelationType: previousStatus?.resultRelationType,\n lastRunTime: previousStatus?.lastRunTime,\n referencedTables: previousStatus?.referencedTables || [],\n };\n }),\n );\n\n try {\n const db = state.db;\n const dbConnectors = state.db.connectors;\n\n if (signal?.aborted) throw new Error('Query cancelled');\n\n const effectiveResultName = getEffectiveResultName(\n cell.data as SqlCellData,\n convertToValidColumnOrTableName,\n );\n const selectedConnectorId = (cell.data as SqlCellData).connectorId;\n let tableName = '';\n let relationType: ResultRelationType = 'view';\n const connector = await db.getConnector();\n if (dbConnectors?.runQuery) {\n const routed = await dbConnectors.runQuery({\n connectionId: selectedConnectorId,\n sql,\n queryType: 'arrow',\n materialize: true,\n materializedName: effectiveResultName,\n materializedSchema: finalSchemaName,\n materializedDatabase: finalDatabaseName,\n signal,\n });\n if (!routed.relationName) {\n throw new Error('Query did not return a materialized relation');\n }\n tableName = routed.relationName;\n relationType = 'table';\n } else {\n const parsed = await db.sqlSelectToJson(sql);\n if (parsed.error) {\n throw new Error(parsed.error_message || 'Not a valid SELECT statement');\n }\n\n if (signal?.aborted) throw new Error('Query cancelled');\n\n await connector.query(\n `CREATE SCHEMA IF NOT EXISTS ${escapeId(finalSchemaName)}`,\n {signal},\n );\n\n tableName = makeQualifiedTableName({\n table: effectiveResultName,\n schema: finalSchemaName,\n database: db.currentDatabase,\n }).toString();\n // Adaptive policy:\n // - first run: choose table when there are downstream dependents,\n // otherwise keep a lightweight view.\n // - subsequent runs: preserve the previously selected relation type.\n const hasDownstream =\n Boolean(sheetId) &&\n getState().cells.getDownstream(sheetId as string, cellId).length > 0;\n relationType = chooseAdaptiveRelationType({\n previousRelationType: previousStatus?.resultRelationType,\n hasDownstream,\n });\n\n if (signal?.aborted) throw new Error('Query cancelled');\n\n await createOrReplaceResultRelation({\n connector,\n relationName: tableName,\n relationType,\n sql,\n signal,\n });\n }\n\n // Find dependencies for referenced tables\n const referenced = findSqlDependencies({\n targetCell: cell,\n cells: Object.fromEntries(cellsInScope.map((c) => [c.id, c])),\n getSqlText: (c) => (isSqlCell(c) ? c.data.sql : undefined),\n getInputVarName: (c) =>\n isInputCell(c) ? c.data.input.varName : undefined,\n getSqlResultName: (id) => {\n const s = getState().cells.status[id];\n if (s?.type !== 'sql') return undefined;\n const fromStatus = getUnqualifiedSqlIdentifier(s.resultName);\n if (fromStatus) return fromStatus;\n\n const currentCell = getState().cells.config.data[id];\n if (currentCell?.type !== 'sql') return undefined;\n return getEffectiveResultName(\n currentCell.data as SqlCellData,\n convertToValidColumnOrTableName,\n );\n },\n });\n\n // 3. Update status to success\n set((s) =>\n produce(s, (draft) => {\n draft.cells.status[cellId] = {\n type: 'sql',\n status: 'success',\n resultName: tableName,\n resultView: tableName,\n resultRelationType: relationType,\n referencedTables: referenced,\n lastRunTime: Date.now(),\n };\n }),\n );\n\n // 4. Fetch count + first page and store result\n if (setCellResult) {\n if (signal?.aborted) throw new Error('Query cancelled');\n\n const countResult = await connector.query(\n `SELECT COUNT(*)::int AS count FROM ${tableName}`,\n {signal},\n );\n const totalRows = countResult.toArray()[0]?.count ?? 0;\n\n if (signal?.aborted) throw new Error('Query cancelled');\n\n const pageResult = await connector.query(\n `SELECT * FROM ${tableName} LIMIT ${DEFAULT_PAGE_SIZE}`,\n {signal},\n );\n\n setCellResult(cellId, {arrowTable: pageResult, totalRows});\n }\n\n // 5. Cascade if needed\n if (cascade) {\n const ownerSheetId = findSheetIdForCell(getState(), cellId);\n if (ownerSheetId) {\n await state.cells.runDownstreamCascade(ownerSheetId, cellId);\n }\n }\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n set((s) =>\n produce(s, (draft) => {\n const status = draft.cells.status[cellId];\n if (status?.type === 'sql') {\n status.status = signal?.aborted ? 'cancel' : 'error';\n status.lastError = message;\n }\n }),\n );\n } finally {\n set((s) =>\n produce(s, (draft) => {\n delete draft.cells.activeAbortControllers[cellId];\n }),\n );\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { Cell, CellRegistry, CellsRootState, CellsSliceConfig, Sheet, SheetType, SqlSelectToJsonFn } from './types';
2
+ /**
3
+ * Helper to resolve dependencies using the registry's async AST-based resolver.
4
+ */
5
+ export declare function resolveDependencies(cell: Cell, cells: Record<string, Cell>, sheetId: string, registry: CellRegistry, sqlSelectToJson: SqlSelectToJsonFn): Promise<string[]>;
6
+ /**
7
+ * Finds the sheet ID that contains the given cell ID.
8
+ */
9
+ export declare function findSheetIdForCell(state: CellsRootState, cellId: string): string | undefined;
10
+ /**
11
+ * Gets all sheets of a specific type.
12
+ */
13
+ export declare function getSheetsByType(state: CellsRootState, type: SheetType): Record<string, Sheet>;
14
+ /**
15
+ * Required accessor for SQL AST parser used in dependency derivation.
16
+ */
17
+ export declare function getRequiredSqlSelectToJson(state: CellsRootState): SqlSelectToJsonFn;
18
+ /**
19
+ * Normalizes structural sheet fields in a partial cells config.
20
+ *
21
+ * This enforces internal invariants before the config is applied:
22
+ * - `sheets` always contains at least one entry (falls back to defaults).
23
+ * - `sheetOrder` only contains existing sheet ids and includes every sheet.
24
+ * - `currentSheetId` always points to an existing sheet.
25
+ *
26
+ * Non-structural fields are preserved by the caller when merging.
27
+ */
28
+ export declare function normalizeCellsConfigStructure(config: Partial<CellsSliceConfig>, defaultConfig: CellsSliceConfig): Pick<CellsSliceConfig, 'sheets' | 'sheetOrder' | 'currentSheetId'>;
29
+ /**
30
+ * Returns the final identifier segment from a possibly-qualified SQL name.
31
+ *
32
+ * The parser is quote-aware: dots inside double-quoted identifiers are treated
33
+ * as part of the identifier rather than as qualification separators.
34
+ *
35
+ * Examples:
36
+ * - `schema.table` -> `table`
37
+ * - `db.schema.table` -> `table`
38
+ * - `schema."my.funny.table"` -> `my.funny.table`
39
+ */
40
+ export declare function getUnqualifiedSqlIdentifier(qualifiedName: string | undefined): string | undefined;
41
+ /**
42
+ * Resolves the schema name for a sheet, falling back to a stable id-based name.
43
+ */
44
+ export declare function resolveSheetSchemaName(sheet: Pick<Sheet, 'id' | 'schemaName'>): string;
45
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,KAAK,EACL,SAAS,EACT,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,EAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,YAAY,EACtB,eAAe,EAAE,iBAAiB,GACjC,OAAO,CAAC,MAAM,EAAE,CAAC,CAInB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,SAAS,CAOpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,cAAc,EACrB,IAAI,EAAE,SAAS,GACd,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAQvB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,cAAc,GACpB,iBAAiB,CAQnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,EACjC,aAAa,EAAE,gBAAgB,GAC9B,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,YAAY,GAAG,gBAAgB,CAAC,CAqBpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,MAAM,GAAG,SAAS,CAuCpB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY,CAAC,UAGxC"}
@@ -0,0 +1,124 @@
1
+ import { getSheetSchemaName } from './utils';
2
+ /**
3
+ * Helper to resolve dependencies using the registry's async AST-based resolver.
4
+ */
5
+ export async function resolveDependencies(cell, cells, sheetId, registry, sqlSelectToJson) {
6
+ const item = registry[cell.type];
7
+ if (!item)
8
+ return [];
9
+ return item.findDependencies({ cell, cells, sheetId, sqlSelectToJson });
10
+ }
11
+ /**
12
+ * Finds the sheet ID that contains the given cell ID.
13
+ */
14
+ export function findSheetIdForCell(state, cellId) {
15
+ for (const [id, sheet] of Object.entries(state.cells.config.sheets)) {
16
+ if (sheet.cellIds.includes(cellId)) {
17
+ return id;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
22
+ /**
23
+ * Gets all sheets of a specific type.
24
+ */
25
+ export function getSheetsByType(state, type) {
26
+ const sheets = {};
27
+ for (const [id, sheet] of Object.entries(state.cells.config.sheets)) {
28
+ if (sheet.type === type) {
29
+ sheets[id] = sheet;
30
+ }
31
+ }
32
+ return sheets;
33
+ }
34
+ /**
35
+ * Required accessor for SQL AST parser used in dependency derivation.
36
+ */
37
+ export function getRequiredSqlSelectToJson(state) {
38
+ const parser = state.db?.sqlSelectToJson;
39
+ if (!parser) {
40
+ throw new Error('cells dependency derivation requires db.sqlSelectToJson. Ensure the DuckDB slice is mounted and initialized before running cell dependency operations.');
41
+ }
42
+ return parser;
43
+ }
44
+ /**
45
+ * Normalizes structural sheet fields in a partial cells config.
46
+ *
47
+ * This enforces internal invariants before the config is applied:
48
+ * - `sheets` always contains at least one entry (falls back to defaults).
49
+ * - `sheetOrder` only contains existing sheet ids and includes every sheet.
50
+ * - `currentSheetId` always points to an existing sheet.
51
+ *
52
+ * Non-structural fields are preserved by the caller when merging.
53
+ */
54
+ export function normalizeCellsConfigStructure(config, defaultConfig) {
55
+ const sheets = config.sheets && Object.keys(config.sheets).length > 0
56
+ ? config.sheets
57
+ : defaultConfig.sheets;
58
+ const sheetIds = Object.keys(sheets);
59
+ const normalizedSheetOrder = config.sheetOrder?.filter((id) => id in sheets) ?? [];
60
+ const missingSheetIds = sheetIds.filter((id) => !normalizedSheetOrder.includes(id));
61
+ const sheetOrder = normalizedSheetOrder.length > 0
62
+ ? [...normalizedSheetOrder, ...missingSheetIds]
63
+ : sheetIds;
64
+ const currentSheetId = (config.currentSheetId && config.currentSheetId in sheets
65
+ ? config.currentSheetId
66
+ : sheetOrder[0]) ?? sheetIds[0];
67
+ return { sheets, sheetOrder, currentSheetId };
68
+ }
69
+ /**
70
+ * Returns the final identifier segment from a possibly-qualified SQL name.
71
+ *
72
+ * The parser is quote-aware: dots inside double-quoted identifiers are treated
73
+ * as part of the identifier rather than as qualification separators.
74
+ *
75
+ * Examples:
76
+ * - `schema.table` -> `table`
77
+ * - `db.schema.table` -> `table`
78
+ * - `schema."my.funny.table"` -> `my.funny.table`
79
+ */
80
+ export function getUnqualifiedSqlIdentifier(qualifiedName) {
81
+ if (!qualifiedName)
82
+ return undefined;
83
+ const input = qualifiedName.trim();
84
+ if (!input)
85
+ return undefined;
86
+ const parts = [];
87
+ let current = '';
88
+ let inQuotes = false;
89
+ for (let i = 0; i < input.length; i += 1) {
90
+ const ch = input[i];
91
+ if (ch === '"') {
92
+ // In SQL identifiers, escaped quotes inside quoted identifiers are doubled.
93
+ if (inQuotes && input[i + 1] === '"') {
94
+ current += '"';
95
+ i += 1;
96
+ continue;
97
+ }
98
+ inQuotes = !inQuotes;
99
+ current += ch;
100
+ continue;
101
+ }
102
+ if (ch === '.' && !inQuotes) {
103
+ parts.push(current);
104
+ current = '';
105
+ continue;
106
+ }
107
+ current += ch;
108
+ }
109
+ parts.push(current);
110
+ const last = parts[parts.length - 1]?.trim();
111
+ if (!last)
112
+ return undefined;
113
+ if (last.startsWith('"') && last.endsWith('"') && last.length >= 2) {
114
+ return last.slice(1, -1).replaceAll('""', '"');
115
+ }
116
+ return last;
117
+ }
118
+ /**
119
+ * Resolves the schema name for a sheet, falling back to a stable id-based name.
120
+ */
121
+ export function resolveSheetSchemaName(sheet) {
122
+ return sheet.schemaName || getSheetSchemaName(sheet.id);
123
+ }
124
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Cell,\n CellRegistry,\n CellsRootState,\n CellsSliceConfig,\n Sheet,\n SheetType,\n SqlSelectToJsonFn,\n} from './types';\nimport {getSheetSchemaName} from './utils';\n\n/**\n * Helper to resolve dependencies using the registry's async AST-based resolver.\n */\nexport async function resolveDependencies(\n cell: Cell,\n cells: Record<string, Cell>,\n sheetId: string,\n registry: CellRegistry,\n sqlSelectToJson: SqlSelectToJsonFn,\n): Promise<string[]> {\n const item = registry[cell.type];\n if (!item) return [];\n return item.findDependencies({cell, cells, sheetId, sqlSelectToJson});\n}\n\n/**\n * Finds the sheet ID that contains the given cell ID.\n */\nexport function findSheetIdForCell(\n state: CellsRootState,\n cellId: string,\n): string | undefined {\n for (const [id, sheet] of Object.entries(state.cells.config.sheets)) {\n if (sheet.cellIds.includes(cellId)) {\n return id;\n }\n }\n return undefined;\n}\n\n/**\n * Gets all sheets of a specific type.\n */\nexport function getSheetsByType(\n state: CellsRootState,\n type: SheetType,\n): Record<string, Sheet> {\n const sheets: Record<string, Sheet> = {};\n for (const [id, sheet] of Object.entries(state.cells.config.sheets)) {\n if (sheet.type === type) {\n sheets[id] = sheet;\n }\n }\n return sheets;\n}\n\n/**\n * Required accessor for SQL AST parser used in dependency derivation.\n */\nexport function getRequiredSqlSelectToJson(\n state: CellsRootState,\n): SqlSelectToJsonFn {\n const parser = state.db?.sqlSelectToJson;\n if (!parser) {\n throw new Error(\n 'cells dependency derivation requires db.sqlSelectToJson. Ensure the DuckDB slice is mounted and initialized before running cell dependency operations.',\n );\n }\n return parser;\n}\n\n/**\n * Normalizes structural sheet fields in a partial cells config.\n *\n * This enforces internal invariants before the config is applied:\n * - `sheets` always contains at least one entry (falls back to defaults).\n * - `sheetOrder` only contains existing sheet ids and includes every sheet.\n * - `currentSheetId` always points to an existing sheet.\n *\n * Non-structural fields are preserved by the caller when merging.\n */\nexport function normalizeCellsConfigStructure(\n config: Partial<CellsSliceConfig>,\n defaultConfig: CellsSliceConfig,\n): Pick<CellsSliceConfig, 'sheets' | 'sheetOrder' | 'currentSheetId'> {\n const sheets =\n config.sheets && Object.keys(config.sheets).length > 0\n ? config.sheets\n : defaultConfig.sheets;\n const sheetIds = Object.keys(sheets);\n const normalizedSheetOrder =\n config.sheetOrder?.filter((id) => id in sheets) ?? [];\n const missingSheetIds = sheetIds.filter(\n (id) => !normalizedSheetOrder.includes(id),\n );\n const sheetOrder =\n normalizedSheetOrder.length > 0\n ? [...normalizedSheetOrder, ...missingSheetIds]\n : sheetIds;\n const currentSheetId =\n (config.currentSheetId && config.currentSheetId in sheets\n ? config.currentSheetId\n : sheetOrder[0]) ?? sheetIds[0];\n\n return {sheets, sheetOrder, currentSheetId};\n}\n\n/**\n * Returns the final identifier segment from a possibly-qualified SQL name.\n *\n * The parser is quote-aware: dots inside double-quoted identifiers are treated\n * as part of the identifier rather than as qualification separators.\n *\n * Examples:\n * - `schema.table` -> `table`\n * - `db.schema.table` -> `table`\n * - `schema.\"my.funny.table\"` -> `my.funny.table`\n */\nexport function getUnqualifiedSqlIdentifier(\n qualifiedName: string | undefined,\n): string | undefined {\n if (!qualifiedName) return undefined;\n const input = qualifiedName.trim();\n if (!input) return undefined;\n\n const parts: string[] = [];\n let current = '';\n let inQuotes = false;\n\n for (let i = 0; i < input.length; i += 1) {\n const ch = input[i];\n if (ch === '\"') {\n // In SQL identifiers, escaped quotes inside quoted identifiers are doubled.\n if (inQuotes && input[i + 1] === '\"') {\n current += '\"';\n i += 1;\n continue;\n }\n inQuotes = !inQuotes;\n current += ch;\n continue;\n }\n\n if (ch === '.' && !inQuotes) {\n parts.push(current);\n current = '';\n continue;\n }\n\n current += ch;\n }\n parts.push(current);\n\n const last = parts[parts.length - 1]?.trim();\n if (!last) return undefined;\n if (last.startsWith('\"') && last.endsWith('\"') && last.length >= 2) {\n return last.slice(1, -1).replaceAll('\"\"', '\"');\n }\n return last;\n}\n\n/**\n * Resolves the schema name for a sheet, falling back to a stable id-based name.\n */\nexport function resolveSheetSchemaName(\n sheet: Pick<Sheet, 'id' | 'schemaName'>,\n) {\n return sheet.schemaName || getSheetSchemaName(sheet.id);\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { DbSliceState } from '@sqlrooms/db';
2
+ import type { CellsSliceState } from './types';
3
+ export type CellsStoreState = {
4
+ cells: CellsSliceState['cells'];
5
+ } & DbSliceState;
6
+ /**
7
+ * Hook to access the cells slice from the room store.
8
+ * This works in any app that includes createCellsSlice in its store.
9
+ */
10
+ export declare function useCellsStore<T>(selector: (state: CellsStoreState) => T): T;
11
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,SAAS,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;CACjC,GAAG,YAAY,CAAC;AAEjB;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG,CAAC,CAE3E"}
package/dist/hooks.js ADDED
@@ -0,0 +1,9 @@
1
+ import { useBaseRoomStore } from '@sqlrooms/room-store';
2
+ /**
3
+ * Hook to access the cells slice from the room store.
4
+ * This works in any app that includes createCellsSlice in its store.
5
+ */
6
+ export function useCellsStore(selector) {
7
+ return useBaseRoomStore(selector);
8
+ }
9
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAOtD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAI,QAAuC;IACtE,OAAO,gBAAgB,CAAqB,QAAQ,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import type {DbSliceState} from '@sqlrooms/db';\nimport {useBaseRoomStore} from '@sqlrooms/room-store';\nimport type {CellsSliceState} from './types';\n\nexport type CellsStoreState = {\n cells: CellsSliceState['cells'];\n} & DbSliceState;\n\n/**\n * Hook to access the cells slice from the room store.\n * This works in any app that includes createCellsSlice in its store.\n */\nexport function useCellsStore<T>(selector: (state: CellsStoreState) => T): T {\n return useBaseRoomStore<CellsStoreState, T>(selector);\n}\n"]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * {@include ../README.md}
3
+ * @packageDocumentation
4
+ */
5
+ import { SqlCellContent as SqlCellContentComponent } from './components/SqlCellContent';
6
+ import { SqlCellRunButton as SqlCellRunButtonComponent } from './components/SqlCellRunButton';
7
+ export { createCellsSlice } from './cellsSlice';
8
+ export type { CellsRootState } from './types';
9
+ export { initializeInput } from './components/Input/helpers';
10
+ export { InputCellContent } from './components/InputCellContent';
11
+ export { SheetsTabBar } from './components/SheetsTabBar';
12
+ export { SqlCellContentComponent as SqlCellContent };
13
+ export { SqlCellRunButtonComponent as SqlCellRunButton };
14
+ export { TextCellContent } from './components/TextCellContent';
15
+ export { VegaCellContent } from './components/VegaCellContent';
16
+ export { createDefaultCellRegistry } from './defaultCellRegistry';
17
+ export { getRenderableDependencyEdges } from './dagUtils';
18
+ export { findSheetIdForCell, getSheetsByType } from './helpers';
19
+ export { useCellsStore } from './hooks';
20
+ export { findSqlDependencies, findSqlDependenciesFromAst, renderSqlWithInputs, runSqlWithCallbacks, } from './sqlHelpers';
21
+ export * from './types';
22
+ export { getEffectiveResultName, isValidSqlIdentifier } from './utils';
23
+ export declare const SqlCell: {
24
+ RunButton: typeof SqlCellRunButtonComponent;
25
+ Content: import("react").FC<import("./components/SqlCellContent").SqlCellContentProps>;
26
+ };
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,cAAc,IAAI,uBAAuB,EAAC,MAAM,6BAA6B,CAAC;AACtF,OAAO,EAAC,gBAAgB,IAAI,yBAAyB,EAAC,MAAM,+BAA+B,CAAC;AAE5F,OAAO,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAC,eAAe,EAAC,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAC,uBAAuB,IAAI,cAAc,EAAC,CAAC;AACnD,OAAO,EAAC,yBAAyB,IAAI,gBAAgB,EAAC,CAAC;AACvD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,yBAAyB,EAAC,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAC,4BAA4B,EAAC,MAAM,YAAY,CAAC;AACxD,OAAO,EAAC,kBAAkB,EAAE,eAAe,EAAC,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAC,aAAa,EAAC,MAAM,SAAS,CAAC;AACtC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAC,sBAAsB,EAAE,oBAAoB,EAAC,MAAM,SAAS,CAAC;AAErE,eAAO,MAAM,OAAO;;;CAGnB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * {@include ../README.md}
3
+ * @packageDocumentation
4
+ */
5
+ import { SqlCellContent as SqlCellContentComponent } from './components/SqlCellContent';
6
+ import { SqlCellRunButton as SqlCellRunButtonComponent } from './components/SqlCellRunButton';
7
+ export { createCellsSlice } from './cellsSlice';
8
+ export { initializeInput } from './components/Input/helpers';
9
+ export { InputCellContent } from './components/InputCellContent';
10
+ export { SheetsTabBar } from './components/SheetsTabBar';
11
+ export { SqlCellContentComponent as SqlCellContent };
12
+ export { SqlCellRunButtonComponent as SqlCellRunButton };
13
+ export { TextCellContent } from './components/TextCellContent';
14
+ export { VegaCellContent } from './components/VegaCellContent';
15
+ export { createDefaultCellRegistry } from './defaultCellRegistry';
16
+ export { getRenderableDependencyEdges } from './dagUtils';
17
+ export { findSheetIdForCell, getSheetsByType } from './helpers';
18
+ export { useCellsStore } from './hooks';
19
+ export { findSqlDependencies, findSqlDependenciesFromAst, renderSqlWithInputs, runSqlWithCallbacks, } from './sqlHelpers';
20
+ export * from './types';
21
+ export { getEffectiveResultName, isValidSqlIdentifier } from './utils';
22
+ export const SqlCell = {
23
+ RunButton: SqlCellRunButtonComponent,
24
+ Content: SqlCellContentComponent,
25
+ };
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,cAAc,IAAI,uBAAuB,EAAC,MAAM,6BAA6B,CAAC;AACtF,OAAO,EAAC,gBAAgB,IAAI,yBAAyB,EAAC,MAAM,+BAA+B,CAAC;AAE5F,OAAO,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAC,eAAe,EAAC,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAC,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAC,YAAY,EAAC,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAC,uBAAuB,IAAI,cAAc,EAAC,CAAC;AACnD,OAAO,EAAC,yBAAyB,IAAI,gBAAgB,EAAC,CAAC;AACvD,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,eAAe,EAAC,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAC,yBAAyB,EAAC,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAC,4BAA4B,EAAC,MAAM,YAAY,CAAC;AACxD,OAAO,EAAC,kBAAkB,EAAE,eAAe,EAAC,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAC,aAAa,EAAC,MAAM,SAAS,CAAC;AACtC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,OAAO,EAAC,sBAAsB,EAAE,oBAAoB,EAAC,MAAM,SAAS,CAAC;AAErE,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,SAAS,EAAE,yBAAyB;IACpC,OAAO,EAAE,uBAAuB;CACjC,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nimport {SqlCellContent as SqlCellContentComponent} from './components/SqlCellContent';\nimport {SqlCellRunButton as SqlCellRunButtonComponent} from './components/SqlCellRunButton';\n\nexport {createCellsSlice} from './cellsSlice';\nexport type {CellsRootState} from './types';\nexport {initializeInput} from './components/Input/helpers';\nexport {InputCellContent} from './components/InputCellContent';\nexport {SheetsTabBar} from './components/SheetsTabBar';\nexport {SqlCellContentComponent as SqlCellContent};\nexport {SqlCellRunButtonComponent as SqlCellRunButton};\nexport {TextCellContent} from './components/TextCellContent';\nexport {VegaCellContent} from './components/VegaCellContent';\nexport {createDefaultCellRegistry} from './defaultCellRegistry';\nexport {getRenderableDependencyEdges} from './dagUtils';\nexport {findSheetIdForCell, getSheetsByType} from './helpers';\nexport {useCellsStore} from './hooks';\nexport {\n findSqlDependencies,\n findSqlDependenciesFromAst,\n renderSqlWithInputs,\n runSqlWithCallbacks,\n} from './sqlHelpers';\nexport * from './types';\nexport {getEffectiveResultName, isValidSqlIdentifier} from './utils';\n\nexport const SqlCell = {\n RunButton: SqlCellRunButtonComponent,\n Content: SqlCellContentComponent,\n};\n"]}
@@ -0,0 +1,74 @@
1
+ import type { QualifiedTableName } from '@sqlrooms/duckdb';
2
+ /**
3
+ * Centralized policy for persisting SQL cell query results.
4
+ *
5
+ * This module implements an adaptive strategy:
6
+ * - first run chooses `table` when downstream dependencies exist, otherwise
7
+ * `view`;
8
+ * - subsequent runs preserve the previously chosen relation type.
9
+ *
10
+ * The policy is shared by execution, rename, and cleanup paths so behavior
11
+ * stays consistent across cross-cutting lifecycle events. Cleanup intentionally
12
+ * drops both relation variants (`VIEW` and `TABLE`) because historical runs or
13
+ * migrations may have created either form for the same logical result name.
14
+ */
15
+ export type ResultRelationType = 'view' | 'table';
16
+ export type ResultRelationName = string | QualifiedTableName;
17
+ type SqlConnectorLike = {
18
+ query: (sql: string, options?: {
19
+ signal?: AbortSignal;
20
+ }) => PromiseLike<unknown>;
21
+ };
22
+ /**
23
+ * Chooses whether a SQL cell result should be stored as a logical view or a
24
+ * materialized table.
25
+ *
26
+ * The policy is sticky: once a relation type has been selected for a cell,
27
+ * later executions preserve that choice. On first execution, downstream usage
28
+ * is used as a proxy for reuse.
29
+ */
30
+ export declare function chooseAdaptiveRelationType(args: {
31
+ previousRelationType?: ResultRelationType;
32
+ hasDownstream: boolean;
33
+ }): ResultRelationType;
34
+ /**
35
+ * Creates or replaces the relation that backs a SQL cell result.
36
+ *
37
+ * - `table`: materializes data for stable repeated reads.
38
+ * - `view`: stores query definition for lightweight freshness.
39
+ */
40
+ export declare function createOrReplaceResultRelation(args: {
41
+ connector: SqlConnectorLike;
42
+ relationName: ResultRelationName;
43
+ relationType: ResultRelationType;
44
+ sql: string;
45
+ signal?: AbortSignal;
46
+ }): Promise<void>;
47
+ /**
48
+ * Drops both view and table variants for a relation name.
49
+ *
50
+ * This is intentionally defensive because different runs may have used
51
+ * different relation modes.
52
+ */
53
+ export declare function dropResultRelation(args: {
54
+ connector: SqlConnectorLike;
55
+ relationName?: ResultRelationName;
56
+ }): Promise<void>;
57
+ /**
58
+ * Renames a saved SQL cell result relation while preserving its mode.
59
+ *
60
+ * - `table`: copies current relation data into the new name.
61
+ * - `view`: recreates the view definition under the new name.
62
+ *
63
+ * Any existing destination relation is removed first, and the old source name
64
+ * is cleaned up after the new relation is created.
65
+ */
66
+ export declare function renameResultRelation(args: {
67
+ connector: SqlConnectorLike;
68
+ oldRelationName: ResultRelationName;
69
+ newRelationName: ResultRelationName;
70
+ relationType: ResultRelationType;
71
+ viewSql: string;
72
+ }): Promise<void>;
73
+ export {};
74
+ //# sourceMappingURL=resultRelationPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resultRelationPolicy.d.ts","sourceRoot":"","sources":["../src/resultRelationPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,kBAAkB,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,CAAC;AAClD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAE7D,KAAK,gBAAgB,GAAG;IACtB,KAAK,EAAE,CACL,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAC,KAC7B,WAAW,CAAC,OAAO,CAAC,CAAC;CAC3B,CAAC;AAMF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,kBAAkB,CAIrB;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,CAAC,IAAI,EAAE;IACxD,SAAS,EAAE,gBAAgB,CAAC;IAC5B,YAAY,EAAE,kBAAkB,CAAC;IACjC,YAAY,EAAE,kBAAkB,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,iBAeA;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC,iBAMA;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,SAAS,EAAE,gBAAgB,CAAC;IAC5B,eAAe,EAAE,kBAAkB,CAAC;IACpC,eAAe,EAAE,kBAAkB,CAAC;IACpC,YAAY,EAAE,kBAAkB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CACjB,iBAkBA"}