create-ncblock 0.0.40 → 0.0.41
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/package.json
CHANGED
package/scripts/init.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { resolveInitArgs } from "./utils/resolveInitArgs"
|
|
|
18
18
|
import {
|
|
19
19
|
getTemplateByName,
|
|
20
20
|
getTemplates,
|
|
21
|
+
isWorkerTemplate,
|
|
21
22
|
scaffoldTemplate,
|
|
22
23
|
type TemplateMetadata,
|
|
23
24
|
} from "./utils/templates"
|
|
@@ -542,6 +543,7 @@ async function main() {
|
|
|
542
543
|
)
|
|
543
544
|
const dest = resolve(dir)
|
|
544
545
|
const templateDir = resolve(templateBaseDir, selectedTemplate.name)
|
|
546
|
+
const workerShaped = isWorkerTemplate(templateDir)
|
|
545
547
|
|
|
546
548
|
const destNonEmpty = existsSync(dest) && readdirSync(dest).length > 0
|
|
547
549
|
if (destNonEmpty) {
|
|
@@ -578,7 +580,10 @@ async function main() {
|
|
|
578
580
|
// downstream walks a positional block/view ID up to its parent database.
|
|
579
581
|
const blockIdArg = args.block as string | undefined
|
|
580
582
|
const promptedCollection =
|
|
581
|
-
|
|
583
|
+
!workerShaped &&
|
|
584
|
+
args.collection === undefined &&
|
|
585
|
+
blockIdArg === undefined &&
|
|
586
|
+
installDeps
|
|
582
587
|
? (await ask("Database URL/ID (enter to skip):", "skip")).trim()
|
|
583
588
|
: undefined
|
|
584
589
|
const collectionOverride =
|
|
@@ -640,12 +645,14 @@ async function main() {
|
|
|
640
645
|
step("Initialized git repo")
|
|
641
646
|
}
|
|
642
647
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
648
|
+
if (!workerShaped) {
|
|
649
|
+
writeInitialTarget(dest, { env: resolved.env, blockId })
|
|
650
|
+
step(`Seeded .notion/target.json`)
|
|
651
|
+
if (classified?.kind === "view") {
|
|
652
|
+
console.log(
|
|
653
|
+
` ${c.dim}(view ID resolved — using as block id, database ${databaseId ?? "(none)"})${c.reset}`,
|
|
654
|
+
)
|
|
655
|
+
}
|
|
649
656
|
}
|
|
650
657
|
|
|
651
658
|
let connected = false
|
|
@@ -660,7 +667,7 @@ async function main() {
|
|
|
660
667
|
execSync(`${pm} ${installArgs}`, { cwd: dest, stdio: "inherit" })
|
|
661
668
|
step("Installed dependencies")
|
|
662
669
|
|
|
663
|
-
if (databaseId) {
|
|
670
|
+
if (databaseId && !workerShaped) {
|
|
664
671
|
console.log("")
|
|
665
672
|
try {
|
|
666
673
|
runNcblock(dest, ["connect", databaseId, "--quiet"])
|
|
@@ -676,8 +683,6 @@ async function main() {
|
|
|
676
683
|
}
|
|
677
684
|
}
|
|
678
685
|
|
|
679
|
-
const deployCommand = blockId ? `npx ncblock deploy dist/` : undefined
|
|
680
|
-
|
|
681
686
|
console.log(`\n${c.bold} Ready!${c.reset} Next steps:\n`)
|
|
682
687
|
if (dest !== resolve(".")) {
|
|
683
688
|
console.log(` cd ${formatShellPath(dest)}`)
|
|
@@ -685,22 +690,33 @@ async function main() {
|
|
|
685
690
|
if (!installDeps) {
|
|
686
691
|
console.log(` ${pm} install`)
|
|
687
692
|
}
|
|
688
|
-
|
|
689
|
-
if (
|
|
690
|
-
|
|
693
|
+
|
|
694
|
+
if (workerShaped) {
|
|
695
|
+
// Workers build and bind their views/data sources as part of deploy; data
|
|
696
|
+
// sources are wired in code (see AGENTS.md). Carry a non-prod env through.
|
|
697
|
+
const envFlag =
|
|
698
|
+
resolved.env === "production" ? "" : ` --env=${resolved.env}`
|
|
699
|
+
console.log(` ntn${envFlag} workers deploy`)
|
|
700
|
+
console.log("")
|
|
691
701
|
} else {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
)
|
|
695
|
-
|
|
702
|
+
const deployCommand = blockId ? `npx ncblock deploy dist/` : undefined
|
|
703
|
+
console.log(` ${pm} run build`)
|
|
704
|
+
if (deployCommand) {
|
|
705
|
+
console.log(` ${deployCommand}`)
|
|
706
|
+
} else {
|
|
707
|
+
console.log(
|
|
708
|
+
`\n Then, go back to the instructions in Notion and paste the ${c.bold}ntn deploy${c.reset} command.`,
|
|
709
|
+
)
|
|
710
|
+
}
|
|
696
711
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
712
|
+
if (installDeps && !connected) {
|
|
713
|
+
console.log(
|
|
714
|
+
`\n ${c.yellow}No data source connected yet.${c.reset} To wire one up:`,
|
|
715
|
+
)
|
|
716
|
+
console.log(` npx ncblock connect <database-url-or-id>`)
|
|
717
|
+
}
|
|
718
|
+
console.log("")
|
|
702
719
|
}
|
|
703
|
-
console.log("")
|
|
704
720
|
|
|
705
721
|
closePromptInterface()
|
|
706
722
|
}
|
|
@@ -117,6 +117,17 @@ export function getTemplateByName(
|
|
|
117
117
|
)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
// A template is worker-backed if @notionhq/workers is in its dependencies.
|
|
121
|
+
export function isWorkerTemplate(templateDir: string): boolean {
|
|
122
|
+
const pkgPath = resolve(templateDir, "package.json")
|
|
123
|
+
if (!existsSync(pkgPath)) {
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
return Boolean(
|
|
127
|
+
readTemplatePackageJson(pkgPath).dependencies?.["@notionhq/workers"],
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
120
131
|
type ScaffoldTemplateOptions = {
|
|
121
132
|
root: string
|
|
122
133
|
templateDir: string
|
|
@@ -293,9 +304,13 @@ export function scaffoldTemplate({
|
|
|
293
304
|
})
|
|
294
305
|
}
|
|
295
306
|
|
|
307
|
+
// Worker templates ship their own AGENTS.md (a symlink copyDirectoryContents
|
|
308
|
+
// skips), so materialize it instead of the generic custom-block guide.
|
|
296
309
|
const agentsTarget = resolve(dest, "AGENTS.md")
|
|
297
310
|
if (overwrite || !existsSync(agentsTarget)) {
|
|
298
|
-
const agentsSource =
|
|
311
|
+
const agentsSource = isWorkerTemplate(templateDir)
|
|
312
|
+
? resolve(templateDir, "AGENTS.md")
|
|
313
|
+
: resolve(root, "scripts/scaffold-assets/AGENTS.md")
|
|
299
314
|
writeRenderedFile({
|
|
300
315
|
sourcePath: agentsSource,
|
|
301
316
|
targetPath: agentsTarget,
|
package/sdk-version.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.0.
|
|
1
|
+
{"version":"0.0.41"}
|
|
@@ -5,6 +5,7 @@ Overall workers documentation lives at https://developers.notion.com/workers/get
|
|
|
5
5
|
## Project Structure & Module Organization
|
|
6
6
|
- `src/index.ts` defines the worker and capabilities.
|
|
7
7
|
- `.examples/` has focused samples (sync, tool, automation, OAuth, webhook).
|
|
8
|
+
- `views/` holds custom-block views (frontend React apps) shipped by the worker. Each view is its own buildable project (`views/<name>/`) with its own `AGENTS.md` describing how to author it. See [Custom blocks (views)](#custom-blocks-views).
|
|
8
9
|
- Shared agent skills live in `.agents/skills/`. `.claude/skills` is kept as a compatibility symlink for Claude-specific discovery.
|
|
9
10
|
- Generated: `dist/` build output, `workers.json` CLI config.
|
|
10
11
|
|
|
@@ -72,6 +73,12 @@ worker.webhook("onGithubPush", {
|
|
|
72
73
|
}
|
|
73
74
|
},
|
|
74
75
|
});
|
|
76
|
+
|
|
77
|
+
// Ship a frontend view (custom block) rendered inside a Notion block.
|
|
78
|
+
// The view lives in views/dashboard/ and deploys with the worker.
|
|
79
|
+
worker.customBlock("dashboard", {
|
|
80
|
+
path: "./views/dashboard",
|
|
81
|
+
});
|
|
75
82
|
```
|
|
76
83
|
|
|
77
84
|
### Notion API access (`context.notion`)
|
|
@@ -367,6 +374,56 @@ This full URL can be retrieved using the `ntn workers webhooks list` command.
|
|
|
367
374
|
|
|
368
375
|
It is also the responsibility of the worker to verify the webhook. Throw `WebhookVerificationError` if the payload is not valid. 5 invalid payloads in a row will cause webhooks to short circuit until redeployed.
|
|
369
376
|
|
|
377
|
+
### Custom blocks (views)
|
|
378
|
+
|
|
379
|
+
A custom block is a **frontend view** — a sandboxed React app rendered inside a Notion block. It ships *with* the worker: there is no separate CLI. `ntn workers deploy` builds the view, bundles it, and binds its data sources in one step. **Do not use the `ncblock` CLI here** (e.g. `npx ncblock connect` / `deploy`) — that flow is for standalone custom-block projects and does not apply inside a worker.
|
|
380
|
+
|
|
381
|
+
Declare a view with `worker.customBlock(key, config)` in `src/index.ts`:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
worker.customBlock("dashboard", {
|
|
385
|
+
path: "./views/dashboard", // buildable project dir, relative to the worker root
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
- `type` defaults to `"project"`: the view is built by running its `command` (default `npm run build`) and its `output` dir (default `dist`) is bundled. Pass `type: "static"` to serve an already-built directory with no build step.
|
|
390
|
+
- The view itself is a separate project under `views/<name>/`. Author it there — see `views/<name>/AGENTS.md` for the frontend rules (React hooks, sizing, forbidden APIs). It uses the `ncblock` package (React hooks + Vite plugin), which is the SDK surface, not the CLI.
|
|
391
|
+
|
|
392
|
+
#### Wiring data sources
|
|
393
|
+
|
|
394
|
+
A view reads data through **managed databases declared in the worker**, not through `ncblock connect`. Declare the database with `worker.database(...)`, then bind it in the `dataSources` map (data-source key → database key). The server binds the block to that managed database on deploy, and the SDK generates the view's `custom_blocks.json` from this declaration — so any hand-written or `ncblock connect`-generated `custom_blocks.json` in the view is ignored.
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
const issues = worker.database("issues", {
|
|
398
|
+
type: "managed",
|
|
399
|
+
initialTitle: "Issues",
|
|
400
|
+
primaryKeyProperty: "Issue ID",
|
|
401
|
+
schema: { properties: { Title: Schema.title(), "Issue ID": Schema.richText() } },
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
worker.sync("issuesSync", { database: issues, execute: async () => { /* ... */ } });
|
|
405
|
+
|
|
406
|
+
worker.customBlock("dashboard", {
|
|
407
|
+
path: "./views/dashboard",
|
|
408
|
+
// Bind the view's "issues" data-source key to the managed `issues` database.
|
|
409
|
+
dataSources: { issues: "issues" },
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Inside the view, read that data source with the `ncblock` hooks (e.g. `useDataSource("issues")`). A view with no `dataSources` is fine — it just renders without host data.
|
|
414
|
+
|
|
415
|
+
#### Developing and deploying
|
|
416
|
+
|
|
417
|
+
```shell
|
|
418
|
+
# Iterate on the view locally (run inside views/<name>/):
|
|
419
|
+
npm run dev # Vite dev server; preview against a live or mock Notion host
|
|
420
|
+
|
|
421
|
+
# Build + deploy the worker and all its views together:
|
|
422
|
+
ntn workers deploy
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
See `views/<name>/AGENTS.md` for preview options (live Notion host vs. mock dev shell).
|
|
426
|
+
|
|
370
427
|
### Sync Management (CLI)
|
|
371
428
|
|
|
372
429
|
**Monitor sync status:**
|
|
@@ -2,30 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
A **Notion custom view** — a sandboxed `<iframe>` rendered inside a Notion block. The iframe is the entire viewport; the only channel to the host is a `postMessage` bridge wrapped by `ncblock`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This view ships as part of a **worker**: it is declared with `worker.customBlock("empty", { path: "./views/empty" })` in the worker's `src/index.ts`, and it builds and deploys together with the worker via `ntn workers deploy`. There is **no separate `ncblock` CLI step** — do not run `npx ncblock connect` / `deploy`. This file covers how to author the view; for how the worker wires and deploys it, see the worker's `AGENTS.md` ("Custom blocks (views)").
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Data sources
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
npx ncblock connect <database-url-or-id>
|
|
11
|
-
```
|
|
9
|
+
This view reads data from **managed databases declared in the worker**, not from a database you connect here. Wiring happens in the worker's `src/index.ts`:
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## Talking to the host
|
|
11
|
+
1. Declare a database with `worker.database(...)`.
|
|
12
|
+
2. Bind it in the `dataSources` map of `worker.customBlock("empty", { path, dataSources: { <viewKey>: <databaseKey> } })`.
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
- Render app code inside `<NotionCustomBlock>` so the handshake completes before hooks like `useTheme` or `useCustomBlockContext` run.
|
|
14
|
+
The worker generates this view's `custom_blocks.json` from that declaration on deploy, so don't hand-edit it (or run `ncblock connect`) — those changes are ignored. Inside the view, read a bound data source with `useDataSource(<viewKey>)`.
|
|
19
15
|
|
|
20
|
-
|
|
16
|
+
## Talking to the host
|
|
21
17
|
|
|
22
|
-
- `
|
|
23
|
-
- `useTheme
|
|
24
|
-
- `useDataSource(key, initialLimit?)` — `{ items, isLoading, hasMore, fetchMore, error }`.
|
|
25
|
-
- `useManifest()` — the declared manifest: data-source keys plus their property declarations.
|
|
26
|
-
- `pages.create(input)` — creates a page; pass `parent: { type: "data_source_key", key }` to target the block's wired data source.
|
|
18
|
+
- Always use the React hooks from `ncblock/react`. Never call `window.parent.postMessage` directly — the SDK owns the protocol.
|
|
19
|
+
- Render app code inside `<NotionCustomBlock>` so the handshake completes before hooks like `useTheme` or `useBlockId` run.
|
|
27
20
|
|
|
28
|
-
`<NotionCustomBlock>` runs `useCustomBlockAutoResize` for you by default — no extra wiring needed. Pass `autoResize={false}` for full-bleed views.
|
|
21
|
+
`<NotionCustomBlock>` runs `useCustomBlockAutoResize` for you by default — no extra wiring needed. Pass `autoResize={false}` for full-bleed views. Read `node_modules/ncblock/README.md` and the `.d.ts` files for current APIs and signatures.
|
|
29
22
|
|
|
30
23
|
## Sizing
|
|
31
24
|
|
|
@@ -41,11 +34,17 @@ Hooks at a glance:
|
|
|
41
34
|
## Conventions
|
|
42
35
|
|
|
43
36
|
- Mount into `<div id="root">` — `useCustomBlockAutoResize` looks for that exact id.
|
|
44
|
-
- `custom_blocks.json`
|
|
37
|
+
- `custom_blocks.json` is the data contract. The SDK's Vite plugin serves it in dev; in a worker it is **generated** from the worker's `worker.customBlock(...)` declaration at deploy time.
|
|
45
38
|
|
|
46
39
|
## Previewing during development
|
|
47
40
|
|
|
48
|
-
|
|
41
|
+
Run the Vite dev server from this view's directory:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run dev # http://localhost:5173
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then preview against a host:
|
|
49
48
|
|
|
50
49
|
1. **Live Notion host** — ask the user to create a block via `/custom` and paste the Vite dev URL (e.g. `http://localhost:5173`) into the block's URL field.
|
|
51
50
|
2. **Mock host** — run the dev shell in another tab:
|
|
@@ -54,14 +53,19 @@ Two options:
|
|
|
54
53
|
cd custom && pnpm install && pnpm run dev:shell # http://localhost:9875
|
|
55
54
|
```
|
|
56
55
|
|
|
56
|
+
When the view is ready, deploy it with the worker from the worker root:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ntn workers deploy
|
|
60
|
+
```
|
|
61
|
+
|
|
57
62
|
## Where to look next
|
|
58
63
|
|
|
59
64
|
- `node_modules/ncblock/README.md` — landing page with a TOC into the per-category docs below.
|
|
60
65
|
- `node_modules/ncblock/docs/lifecycle.md` — `<NotionCustomBlock>`, init, sizing, auto-resize.
|
|
61
|
-
- `node_modules/ncblock/docs/
|
|
66
|
+
- `node_modules/ncblock/docs/block-location.md` — `useBlockId`, `useParent`, `usePage`, `useTheme`.
|
|
62
67
|
- `node_modules/ncblock/docs/data-sources.md` — `useDataSource`, row/property/date types, worked example.
|
|
63
68
|
- `node_modules/ncblock/docs/pages.md` — `pages.create / get / update / delete`.
|
|
64
69
|
- `node_modules/ncblock/docs/users.md` — `users.list / get`, `NotionUser`.
|
|
65
70
|
- `node_modules/ncblock/docs/manifest.md` — `custom_blocks.json` + Vite plugin.
|
|
66
|
-
- `node_modules/ncblock/dist/*.d.ts` — typed surface; hover in your editor.
|
|
67
71
|
- https://github.com/makenotion/custom — source and examples.
|