@xeplr/actions 1.1.23 → 1.2.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/README.md CHANGED
@@ -87,7 +87,7 @@ var moved = await actions.runAction(actions.builtins.dbMove, {
87
87
  |---|---|
88
88
  | `name` | Required. |
89
89
  | `description` | Text for pickers. |
90
- | `inputSchema` | `[{ name, type, required, default, description, order, ... }]`, validated by `@xeplr/schema-handler` `applySchema`. Built-ins also use `group`, `showWhen`, `system`, `options`, `optionsFrom`, `dependsOn` — hints for a form; validation treats every field the same. |
90
+ | `inputSchema` | `[{ name, type, required, default, description, order, ... }]`, validated by `@xeplr/schema-handler` `applySchema`. Built-ins also use `group`, `showWhen`, `system`, `options`, `optionsFrom`, `dependsOn`, `label`, `hidden` (left out of a form unless a value is already set) and `control` (`'parameters'`: `[{ name, value }]` shown as rows, a value fixed or bound to the run's window: `{window.from}` / `{window.to}`, see `@xeplr/jobs`) — hints for a form; validation treats every field the same. |
91
91
  | `outputSchema` | Optional documentation of the return value. |
92
92
  | `requires` | Module names checked with `require.resolve` at `register()`. |
93
93
  | `execute({ input, system, metaStore })` | Returns (or resolves to) the output. |
@@ -159,7 +159,7 @@ For `mode: 'table'` the source's declared column types (`getTableSchema`) are pa
159
159
  | `mysql` | `CALL name(?, ?)` — positional; names are ignored (and logged as such) |
160
160
  | `postgres` | `SELECT * FROM fn(param => …)` when `returnsRows` (default `true`), `CALL proc(param => …)` when `false` |
161
161
 
162
- `params` is `[{ name, value }]` or bare values; values are always bound. `''` becomes `null`, integer- and decimal-looking strings become numbers, and an object value is an error naming the parameter. A schema-qualified name (`dbo.usp_x`) is quoted per part. `window: { from, to, fromParam, toParam }` appends the two bounds as parameters after `params` (null edges passed as `NULL`); a window without `fromParam`/`toParam` is refused. `maxRows` fails the run when more rows come back. If `system.log` is supplied it gets the statement, parameter count and window bounds — not the other parameter values.
162
+ `params` is `[{ name, value }]` or bare values; values are always bound. `''` becomes `null`, integer- and decimal-looking strings become numbers, and an object value is an error naming the parameter. A schema-qualified name (`dbo.usp_x`) is quoted per part. `window: { from, to, fromParam, toParam }` appends the two bounds as parameters after `params` (null edges passed as `NULL`); a window without `fromParam`/`toParam` is refused. From a job, the usual way to pass dates is as ordinary parameters bound to the run's window, `{ "name": "startDate", "value": "{window.from}" }` (`@xeplr/jobs` fills them in); `params` carries `control: 'parameters'` and `window` is `hidden`, so a form shows rows instead of JSON. `maxRows` fails the run when more rows come back. If `system.log` is supplied it gets the statement, parameter count and window bounds — not the other parameter values.
163
163
 
164
164
  ### Email
165
165
 
@@ -348,6 +348,10 @@ On Postgres, MySQL and SQL Server the staging table is shaped like the target (`
348
348
 
349
349
  **Before loading: the file against the table.** `uploader.compareToTable({ driver, connection, targetTable, columns })` → `{ exists, matched, extra, missing: [{ name, required }] }`, matched by exact name, the framework's columns left out. `extra` (in the file, not the table) means the import will be refused; `missing` columns are left empty, and `required` ones (no empty value, no default) mean every row will be refused. `formats.columnsOf(format, filePath, { sheet? })` gives a file's columns from its first row. Every driver's `getTableSchema` now says `nullable` and `hasDefault` (an identity or auto-increment counts as a default).
350
350
 
351
+ ### The uploader's own tables
352
+
353
+ Beside the tables it loads, the uploader makes three of its own: `<table>_import_errors` (refused rows), `<table>__xeplr_staging` (a staged load in progress) and, in a DuckDB file, `__xeplr_loads` (its ledger). `isInternalTable(name)` (also accepts a listing row `{ name }`) says whether a table is one of these, so an app listing a database's tables for people to pick from can leave them out. They still exist and are read where they belong.
354
+
351
355
  ### A written DuckDB file keeps no index
352
356
 
353
357
  DuckDB 1.5.4–1.5.6 has a bug ([duckdb/duckdb#26106](https://github.com/duckdb/duckdb/issues/26106), open upstream): when a process dies with rows still only in the WAL, the next open replays them into the table and the next checkpoint drops them from every index on it. The first `DELETE` that reaches one fails with *"Failed to delete all rows from index"*, which is **FATAL**: the whole file is unusable until the process restarts. Any index, unique or not. A table with no index is unaffected.
package/index.js CHANGED
@@ -37,6 +37,8 @@ module.exports = {
37
37
  // their own — e.g. a UI wizard showing inferred columns from a CSV sample
38
38
  // before actually committing to an import).
39
39
  uploader: uploader,
40
+ // The uploader's own tables (_import_errors, __xeplr_staging, __xeplr_loads), to leave out of table pickers.
41
+ isInternalTable: require('./lib/drivers/db/internalTables').isInternalTable,
40
42
  inferColumns: uploader.inferColumns,
41
43
  streaming: streaming,
42
44
  drivers: { db: dbDrivers, file: fileDrivers },
@@ -89,13 +89,19 @@ module.exports = {
89
89
  // Half-open, from <= x < to. The next run starts at exactly this run's
90
90
  // `to`, so a procedure treating `to` as inclusive double-counts the
91
91
  // boundary row every time.
92
- { name: 'window', type: 'object', order: 6, group: 'Incremental',
92
+ // `hidden`: a form leaves it out unless a job already holds one. A job's
93
+ // dates now reach a procedure as ordinary parameters bound to the run's
94
+ // window — see `params` — which needs no names typed into JSON here.
95
+ { name: 'window', type: 'object', order: 6, group: 'Incremental', hidden: true,
93
96
  description: '{ from, to, fromParam, toParam } — the period this run covers, passed as the ' +
94
97
  'named parameters fromParam/toParam. Filled automatically for a job with ' +
95
98
  'incrementalMode "period"; name the two parameters and leave from/to empty. ' +
96
99
  'A window without fromParam/toParam is refused rather than silently dropped.' },
97
100
 
98
- { name: 'params', type: 'array', order: 7,
101
+ // `control: 'parameters'`: a form shows these as rows — a name and where
102
+ // its value comes from (a fixed value, or the start / end of the run's
103
+ // window, which a job binds as "{window.from}" / "{window.to}").
104
+ { name: 'params', type: 'array', order: 7, control: 'parameters', label: 'Parameters',
99
105
  description: 'Parameters, as [{ name, value }] or bare values. Named entries are matched by ' +
100
106
  'name on postgres and mssql; MySQL has no named-argument syntax, so there they ' +
101
107
  'are positional and must be in the order the procedure declares them.' },
@@ -0,0 +1,31 @@
1
+ // THE TABLES THE UPLOADER MAKES FOR ITSELF, beside the tables it loads:
2
+ //
3
+ // <table>_import_errors rows the database refused (errorRows.js)
4
+ // <table>__xeplr_staging a staged load's rows until they move in (staging.js, duckdb.js)
5
+ // __xeplr_loads a DuckDB file's ledger of staged loads (duckdb.js)
6
+ //
7
+ // They are the uploader's bookkeeping, not data anybody models: an app that
8
+ // lists a database's tables for people to pick from (a dataset builder, a
9
+ // cube's source picker) leaves them out with isInternalTable(). They still
10
+ // exist and are read where they belong (an import's dropped rows).
11
+
12
+ var ERRORS_SUFFIX = '_import_errors';
13
+ var STAGING_SUFFIX = require('./staging').SUFFIX;
14
+ var LOADS_LEDGER = '__xeplr_loads';
15
+
16
+ function endsWith(s, suffix) {
17
+ return s.length > suffix.length && s.slice(-suffix.length) === suffix;
18
+ }
19
+
20
+ /** Whether a table is one the uploader made for itself. Case as the database reports it. */
21
+ function isInternalTable(name) {
22
+ var s = String(name == null ? '' : (typeof name === 'object' ? name.name || name.table || '' : name));
23
+ return endsWith(s, ERRORS_SUFFIX) || endsWith(s, STAGING_SUFFIX) || s === LOADS_LEDGER;
24
+ }
25
+
26
+ module.exports = {
27
+ isInternalTable: isInternalTable,
28
+ ERRORS_SUFFIX: ERRORS_SUFFIX,
29
+ STAGING_SUFFIX: STAGING_SUFFIX,
30
+ LOADS_LEDGER: LOADS_LEDGER
31
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xeplr/actions",
3
- "version": "1.1.23",
3
+ "version": "1.2.0",
4
4
  "description": "Named-action registry with typed inputs and safe invocation — plus built-in actions for HTTP, DB, files, email, spawn",
5
5
  "main": "index.js",
6
6
  "files": [