@notionhq/custom-blocks-dev-shell 0.1.66 → 0.1.67
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 +3 -0
- package/dist/assets/{index-BtBedBtp.js → index-jvcNWLYF.js} +3 -3
- package/dist/index.html +1 -1
- package/dist-cli/dev-shell-launcher.js +14 -1
- package/dist-cli/resolve-bindings.js +39 -0
- package/dist-cli/source-bindings.js +17 -0
- package/docs/bindings.md +43 -19
- package/docs/data-sources.md +20 -0
- package/package.json +1 -1
package/dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Workers local shell</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-jvcNWLYF.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-BOt07-3a.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -14,6 +14,8 @@ import { formatUnknownErrorWithStack } from "./errors.js";
|
|
|
14
14
|
import { materializeWorkerSchemaDataSources } from "./materialize.js";
|
|
15
15
|
import { BLOCK_BASE_PORT, parsePort, SHELL_PORT, validateBlockPortRange, validateShellPort, } from "./ports.js";
|
|
16
16
|
import { installProcessSignalHandlers, ProcessSupervisor, } from "./process-supervisor.js";
|
|
17
|
+
import { resolveLaunchBindings } from "./resolve-bindings.js";
|
|
18
|
+
import { parseBindingOverride, } from "./source-bindings.js";
|
|
17
19
|
import { parseCliArgs } from "./utils.js";
|
|
18
20
|
import { writeBlockViteConfig } from "./vite-block-server.js";
|
|
19
21
|
import { blockCapabilities, findWorkerDir, generateWorkerManifest, } from "./worker-manifest.js";
|
|
@@ -59,11 +61,15 @@ export function parseWorkerLaunchArgs(argv) {
|
|
|
59
61
|
worker: undefined,
|
|
60
62
|
shellPort: SHELL_PORT,
|
|
61
63
|
blockBasePort: BLOCK_BASE_PORT,
|
|
64
|
+
bindings: [],
|
|
62
65
|
};
|
|
63
66
|
let hasPositionalWorker = false;
|
|
64
67
|
parseCliArgs({
|
|
65
68
|
argv,
|
|
66
69
|
handlers: {
|
|
70
|
+
"--bind": value => {
|
|
71
|
+
args.bindings.push(parseBindingOverride(value));
|
|
72
|
+
},
|
|
67
73
|
"--worker": value => {
|
|
68
74
|
if (value.length === 0) {
|
|
69
75
|
throw new Error("--worker requires a path to a worker directory.");
|
|
@@ -83,7 +89,7 @@ export function parseWorkerLaunchArgs(argv) {
|
|
|
83
89
|
},
|
|
84
90
|
onUnknown: arg => {
|
|
85
91
|
throw new Error(`Unknown dev shell option "${arg}". Supported: --worker <dir>, ` +
|
|
86
|
-
`--port <port>, --block-base-port <port>.`);
|
|
92
|
+
`--port <port>, --block-base-port <port>, --bind <capabilityKey>.<dataSourceKey>=<sampleDataFileName>.`);
|
|
87
93
|
},
|
|
88
94
|
onPositional: arg => {
|
|
89
95
|
// The published launcher passes its optional worker path positionally.
|
|
@@ -186,6 +192,13 @@ async function prepareWorkerLaunch(options) {
|
|
|
186
192
|
const plan = await prepareWorkerLaunchPlan(workerDir, {
|
|
187
193
|
blockBasePort: args.blockBasePort,
|
|
188
194
|
});
|
|
195
|
+
const { registryBindings } = resolveLaunchBindings(plan.blocks.map(block => block.capability), plan.dataSources, args.bindings);
|
|
196
|
+
for (const entry of plan.registry) {
|
|
197
|
+
const bindings = registryBindings.get(entry.key);
|
|
198
|
+
if (bindings !== undefined) {
|
|
199
|
+
entry.sampleFilesByDataSource = bindings;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
189
202
|
logWorkerPlan(workerName, plan);
|
|
190
203
|
validateShellPort(args.shellPort, args.blockBasePort, plan.blocks.length);
|
|
191
204
|
const shell = await options.createShellLaunch({
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Validate explicit file selections before servers start. */
|
|
2
|
+
export function resolveLaunchBindings(blocks, sources, overrides) {
|
|
3
|
+
const targets = blocks.flatMap(block => Object.entries(block.config.manifest.dataSources).map(([slotKey]) => ({
|
|
4
|
+
blockKey: block.key,
|
|
5
|
+
slotKey,
|
|
6
|
+
target: `${block.key}.${slotKey}`,
|
|
7
|
+
})));
|
|
8
|
+
const explicit = validateOverrides(targets.map(candidate => candidate.target), sources, overrides);
|
|
9
|
+
const registryBindings = new Map();
|
|
10
|
+
for (const { blockKey, slotKey, target } of targets) {
|
|
11
|
+
const source = explicit.get(target);
|
|
12
|
+
if (source === undefined) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const bindings = registryBindings.get(blockKey) ??
|
|
16
|
+
Object.create(null);
|
|
17
|
+
bindings[slotKey] = `${source.filename}.json`;
|
|
18
|
+
registryBindings.set(blockKey, bindings);
|
|
19
|
+
}
|
|
20
|
+
return { registryBindings };
|
|
21
|
+
}
|
|
22
|
+
function validateOverrides(targets, sources, overrides) {
|
|
23
|
+
const explicit = new Map();
|
|
24
|
+
for (const { target, filename } of overrides) {
|
|
25
|
+
const matches = targets.filter(candidate => candidate === target);
|
|
26
|
+
if (matches.length !== 1) {
|
|
27
|
+
throw new Error(`${matches.length === 0 ? "Unknown" : "Ambiguous"} binding target "${target}". Available targets: ${targets.join(", ") || "(none)"}.`);
|
|
28
|
+
}
|
|
29
|
+
if (explicit.has(target)) {
|
|
30
|
+
throw new Error(`Duplicate --bind target "${target}". Specify each target once.`);
|
|
31
|
+
}
|
|
32
|
+
const source = sources.find(candidate => `${candidate.filename}.json` === filename);
|
|
33
|
+
if (source === undefined) {
|
|
34
|
+
throw new Error(`Cannot bind ${target}: data/${filename} does not exist. Available files: ${sources.map(candidate => `${candidate.filename}.json`).join(", ") || "(none)"}.`);
|
|
35
|
+
}
|
|
36
|
+
explicit.set(target, source);
|
|
37
|
+
}
|
|
38
|
+
return explicit;
|
|
39
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function parseBindingOverride(value) {
|
|
2
|
+
const separator = value.indexOf("=");
|
|
3
|
+
const target = value.slice(0, separator);
|
|
4
|
+
const filename = value.slice(separator + 1);
|
|
5
|
+
const dot = target.indexOf(".");
|
|
6
|
+
if (separator < 0 ||
|
|
7
|
+
dot <= 0 ||
|
|
8
|
+
dot === target.length - 1 ||
|
|
9
|
+
target !== target.trim() ||
|
|
10
|
+
filename !== filename.trim() ||
|
|
11
|
+
!filename.endsWith(".json") ||
|
|
12
|
+
filename === ".json" ||
|
|
13
|
+
/[\\/\0]/.test(filename)) {
|
|
14
|
+
throw new Error("--bind requires <capabilityKey>.<dataSourceKey>=<sampleDataFileName.json>. Use a filename in data/.");
|
|
15
|
+
}
|
|
16
|
+
return { target, filename };
|
|
17
|
+
}
|
package/docs/bindings.md
CHANGED
|
@@ -1,36 +1,60 @@
|
|
|
1
|
-
# Dev
|
|
1
|
+
# Dev shell bindings
|
|
2
2
|
|
|
3
|
-
The shell
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
The dev shell previews your worker's custom blocks with local data from
|
|
4
|
+
`data/*.json`. Each data source selects its file in this order:
|
|
5
|
+
|
|
6
|
+
1. The explicit `--bind` selection.
|
|
7
|
+
2. `data/worker_<dataSourceKey>.json`, if it exists.
|
|
8
|
+
3. `data/<dataSourceKey>.json`, if it exists.
|
|
9
|
+
4. Otherwise, it stays unbound. The shell starts, but the block cannot initialize
|
|
10
|
+
until you connect the source in **Data**.
|
|
11
|
+
|
|
12
|
+
An invalid explicit selection stops startup instead of falling back to another
|
|
13
|
+
file. An empty file still takes precedence over later choices.
|
|
14
|
+
|
|
15
|
+
## Choose sample data
|
|
16
|
+
|
|
17
|
+
Use `--bind <capabilityKey>.<dataSourceKey>=<sampleDataFileName>` to choose a source:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
ntn workers customblocks dev -- --bind task-board.tasks=sample_tasks.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This connects the `tasks` data source on `task-board` to `data/sample_tasks.json`.
|
|
24
|
+
Use a filename from `data/`, including `.json`.
|
|
25
|
+
|
|
26
|
+
Repeat `--bind` for additional data sources. Sources without an override keep
|
|
27
|
+
their defaults. When running `custom-blocks-dev-shell` directly, omit the `--`
|
|
28
|
+
separator.
|
|
29
|
+
|
|
30
|
+
An unknown target, duplicate target, or missing file stops startup.
|
|
31
|
+
Invalid JSON or an invalid source file also stops startup.
|
|
32
|
+
|
|
33
|
+
The browser maps compatible properties automatically. Incomplete mappings do
|
|
34
|
+
not stop the shell. Open **Data** to finish connecting the block.
|
|
35
|
+
Empty rows are valid.
|
|
7
36
|
|
|
8
37
|
## Connecting data
|
|
9
38
|
|
|
10
|
-
**
|
|
39
|
+
**Data** in the toolbar opens the bindings modal: one section
|
|
11
40
|
per slot from the block's `worker.customBlock({ dataSources })` declaration,
|
|
12
41
|
each with a source picker and a per-property mapping. The sources on offer
|
|
13
42
|
are the ones in your worker's `data/*.json`
|
|
14
|
-
(format: [`data-sources.md`](./data-sources.md))
|
|
15
|
-
|
|
43
|
+
(format: [`data-sources.md`](./data-sources.md)). The modal shows each slot
|
|
44
|
+
and its property mappings.
|
|
16
45
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
after trim and lowercasing. Bind when there is exactly one candidate;
|
|
21
|
-
anything ambiguous or absent stays unmapped. Re-picking a source resets the
|
|
22
|
-
slot's mappings and re-runs auto-map. Manual mapping offers only source
|
|
23
|
-
properties of exactly the required type — no coercion.
|
|
46
|
+
Properties map automatically when there is a unique source property with a
|
|
47
|
+
matching key or name and the same type. Use the property pickers to adjust
|
|
48
|
+
mappings. Selecting a different source resets its property mappings.
|
|
24
49
|
|
|
25
50
|
## Initialization & binding edits
|
|
26
51
|
|
|
27
52
|
Once every slot is bound and every property mapped, the block initializes and
|
|
28
|
-
renders.
|
|
29
|
-
|
|
30
|
-
failed to initialize recovers the same way — fix its bindings and save.
|
|
53
|
+
renders. Saving binding changes restarts the block with the updated data.
|
|
54
|
+
If initialization fails, fix the bindings and save.
|
|
31
55
|
|
|
32
56
|
Bindings last for the page load, per block: refresh the shell and every block
|
|
33
|
-
returns to
|
|
57
|
+
returns to its CLI selection or filename default.
|
|
34
58
|
|
|
35
59
|
The shell reads `data/*.json` once at spin-up. To pick up file edits,
|
|
36
60
|
restart the dev-shell command; a browser refresh is not enough.
|
package/docs/data-sources.md
CHANGED
|
@@ -11,6 +11,8 @@ supports the following types today:
|
|
|
11
11
|
|
|
12
12
|
## File format
|
|
13
13
|
|
|
14
|
+
For example, `data/tasks.json`:
|
|
15
|
+
|
|
14
16
|
```json
|
|
15
17
|
{
|
|
16
18
|
"type": "worker",
|
|
@@ -92,3 +94,21 @@ schema with empty `rows`, mirroring a managed database before any sync.
|
|
|
92
94
|
schema.
|
|
93
95
|
|
|
94
96
|
The shell shows exactly the sources in `data/`, nothing else.
|
|
97
|
+
|
|
98
|
+
## Sample rows into a generated source
|
|
99
|
+
|
|
100
|
+
To populate a declared slot, use the generated file as the sample target:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ntn workers customblocks sample <url-or-data-source-id> \
|
|
104
|
+
--out data/worker_tasks.json --row-only
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Run the dev shell once to create the generated file, then stop it before sampling.
|
|
108
|
+
`--row-only` preserves the schema and appends rows with new IDs. Add `--overwrite`
|
|
109
|
+
to replace existing rows.
|
|
110
|
+
|
|
111
|
+
Sampled property keys must match the generated schema. If they differ, sample
|
|
112
|
+
into a separate file and select it with [`--bind`](./bindings.md#choose-sample-data).
|
|
113
|
+
|
|
114
|
+
Restart the dev shell after sampling to load the updated data.
|