@notionhq/apps 0.0.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 (74) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +30 -0
  3. package/dist/cli/build.d.ts +13 -0
  4. package/dist/cli/build.d.ts.map +1 -0
  5. package/dist/cli/build.js +36 -0
  6. package/dist/cli/build.test.d.ts +2 -0
  7. package/dist/cli/build.test.d.ts.map +1 -0
  8. package/dist/cli/codegen.d.ts +10 -0
  9. package/dist/cli/codegen.d.ts.map +1 -0
  10. package/dist/cli/codegen.js +41 -0
  11. package/dist/cli/codegen.test.d.ts +2 -0
  12. package/dist/cli/codegen.test.d.ts.map +1 -0
  13. package/dist/cli/discover.d.ts +32 -0
  14. package/dist/cli/discover.d.ts.map +1 -0
  15. package/dist/cli/discover.js +37 -0
  16. package/dist/cli/discover.test.d.ts +2 -0
  17. package/dist/cli/discover.test.d.ts.map +1 -0
  18. package/dist/cli/emit-manifest.d.ts +10 -0
  19. package/dist/cli/emit-manifest.d.ts.map +1 -0
  20. package/dist/cli/emit-manifest.js +131 -0
  21. package/dist/cli/emit-manifest.test.d.ts +2 -0
  22. package/dist/cli/emit-manifest.test.d.ts.map +1 -0
  23. package/dist/cli/index.d.ts +3 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +36 -0
  26. package/dist/context.d.ts +7 -0
  27. package/dist/context.d.ts.map +1 -0
  28. package/dist/context.js +30 -0
  29. package/dist/error.d.ts +8 -0
  30. package/dist/error.d.ts.map +1 -0
  31. package/dist/error.js +11 -0
  32. package/dist/manifest.d.ts +25 -0
  33. package/dist/manifest.d.ts.map +1 -0
  34. package/dist/manifest.js +0 -0
  35. package/dist/output.d.ts +7 -0
  36. package/dist/output.d.ts.map +1 -0
  37. package/dist/output.js +8 -0
  38. package/dist/runtime-metadata.d.ts +9 -0
  39. package/dist/runtime-metadata.d.ts.map +1 -0
  40. package/dist/runtime-metadata.js +33 -0
  41. package/dist/runtime-metadata.test.d.ts +2 -0
  42. package/dist/runtime-metadata.test.d.ts.map +1 -0
  43. package/dist/triggers.d.ts +365 -0
  44. package/dist/triggers.d.ts.map +1 -0
  45. package/dist/triggers.generated.d.ts +246 -0
  46. package/dist/triggers.generated.d.ts.map +1 -0
  47. package/dist/triggers.generated.js +239 -0
  48. package/dist/triggers.js +0 -0
  49. package/dist/workflow.d.ts +125 -0
  50. package/dist/workflow.d.ts.map +1 -0
  51. package/dist/workflow.js +210 -0
  52. package/dist/workflow.test.d.ts +2 -0
  53. package/dist/workflow.test.d.ts.map +1 -0
  54. package/docs/BUILD.md +66 -0
  55. package/package.json +74 -0
  56. package/src/cli/build.test.ts +73 -0
  57. package/src/cli/build.ts +57 -0
  58. package/src/cli/codegen.test.ts +21 -0
  59. package/src/cli/codegen.ts +51 -0
  60. package/src/cli/discover.test.ts +26 -0
  61. package/src/cli/discover.ts +73 -0
  62. package/src/cli/emit-manifest.test.ts +45 -0
  63. package/src/cli/emit-manifest.ts +190 -0
  64. package/src/cli/index.ts +41 -0
  65. package/src/context.ts +49 -0
  66. package/src/error.ts +12 -0
  67. package/src/manifest.ts +26 -0
  68. package/src/output.ts +8 -0
  69. package/src/runtime-metadata.test.ts +54 -0
  70. package/src/runtime-metadata.ts +46 -0
  71. package/src/triggers.generated.ts +489 -0
  72. package/src/triggers.ts +500 -0
  73. package/src/workflow.test.ts +788 -0
  74. package/src/workflow.ts +418 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Notion Labs, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Apps SDK
2
+
3
+ ## Development
4
+
5
+ 1. Install [mise](https://mise.jdx.dev/getting-started.html)
6
+ 2. Run `mise install`
7
+ 3. Run `mise run install`
8
+ 4. Run `mise run check && mise run test`
9
+
10
+ The SDK requires Node 26 or newer and supports workflow capabilities only:
11
+
12
+ ```ts
13
+ import { triggers } from "@notionhq/apps/triggers";
14
+ import { createWorkflow } from "@notionhq/apps/workflow";
15
+ ```
16
+
17
+ ## Publishing New Versions
18
+
19
+ Increment the package version and create a new Git tag:
20
+
21
+ ```shell
22
+ # This also creates a Git tag.
23
+ mise run version:apps patch
24
+ ```
25
+
26
+ Then, push the commit and tag:
27
+
28
+ ```shell
29
+ git push origin main --follow-tags
30
+ ```
@@ -0,0 +1,13 @@
1
+ import type { AppManifest } from "../manifest.js";
2
+ export type BuildResult = {
3
+ manifest: AppManifest;
4
+ bundlePath: string;
5
+ manifestPath: string;
6
+ };
7
+ /**
8
+ * Build an app project: discover capability files by convention, generate
9
+ * the entry module, bundle it into a single `dist/worker.js`, and emit
10
+ * `dist/manifest.json` describing every capability.
11
+ */
12
+ export declare function buildApp(projectRoot: string): Promise<BuildResult>;
13
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKlD,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAmCxE"}
@@ -0,0 +1,36 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { rolldown } from "rolldown";
4
+ import { generateEntry } from "./codegen.js";
5
+ import { discoverCapabilities } from "./discover.js";
6
+ import { extractManifest } from "./emit-manifest.js";
7
+ async function buildApp(projectRoot) {
8
+ const capabilities = await discoverCapabilities(projectRoot);
9
+ if (capabilities.length === 0) {
10
+ throw new Error(
11
+ `No capabilities found in ${projectRoot}. Add a workflow file under src/workflows/<name>.ts that default-exports createWorkflow(...).`
12
+ );
13
+ }
14
+ const entryPath = path.join(projectRoot, ".notion", "entry.ts");
15
+ await fs.promises.mkdir(path.dirname(entryPath), { recursive: true });
16
+ await fs.promises.writeFile(entryPath, generateEntry(capabilities));
17
+ const bundlePath = path.join(projectRoot, "dist", "worker.js");
18
+ const bundle = await rolldown({
19
+ input: entryPath,
20
+ platform: "node",
21
+ external: (id) => !id.startsWith(".") && !path.isAbsolute(id)
22
+ });
23
+ try {
24
+ await bundle.write({ format: "esm", file: bundlePath });
25
+ } finally {
26
+ await bundle.close();
27
+ }
28
+ const manifest = await extractManifest(bundlePath, capabilities);
29
+ const manifestPath = path.join(projectRoot, "dist", "manifest.json");
30
+ await fs.promises.writeFile(manifestPath, `${JSON.stringify(manifest, null, " ")}
31
+ `);
32
+ return { manifest, bundlePath, manifestPath };
33
+ }
34
+ export {
35
+ buildApp
36
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=build.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.test.d.ts","sourceRoot":"","sources":["../../src/cli/build.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { DiscoveredCapability } from "./discover.js";
2
+ /**
3
+ * Generate the app entry module for a set of discovered capabilities.
4
+ *
5
+ * The module uses only static imports so that the bundler can bundle the
6
+ * whole app into a single file. It is written to `.notion/entry.ts` in
7
+ * the app project, so imports are relative to that location.
8
+ */
9
+ export declare function generateEntry(capabilities: readonly DiscoveredCapability[]): string;
10
+ //# sourceMappingURL=codegen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/cli/codegen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,SAAS,oBAAoB,EAAE,GAAG,MAAM,CAyCnF"}
@@ -0,0 +1,41 @@
1
+ function generateEntry(capabilities) {
2
+ const imports = [];
3
+ const registryEntries = /* @__PURE__ */ new Map();
4
+ capabilities.forEach((capability, index) => {
5
+ const binding = `capability_${index}`;
6
+ imports.push(`import ${binding} from ${JSON.stringify(`../${capability.sourcePath}`)};`);
7
+ const entries = registryEntries.get(capability.type) ?? [];
8
+ entries.push(` ${JSON.stringify(capability.key)}: ${binding},`);
9
+ registryEntries.set(capability.type, entries);
10
+ });
11
+ const registry = Array.from(registryEntries.entries()).map(([type, entries]) => ` ${JSON.stringify(type)}: {
12
+ ${entries.join("\n")}
13
+ },`).join("\n");
14
+ return `// Generated by \`notion-apps build\`. Do not edit.
15
+ ${imports.join("\n")}
16
+
17
+ type Handler = (context?: unknown, options?: { concreteOutput?: true }) => Promise<unknown>;
18
+
19
+ export const capabilities: Record<string, Record<string, { handler: Handler }>> = {
20
+ ${registry}
21
+ };
22
+
23
+ export async function run(
24
+ type: string,
25
+ key: string,
26
+ context?: unknown,
27
+ options?: { concreteOutput?: true },
28
+ ): Promise<unknown> {
29
+ const capability = capabilities[type]?.[key];
30
+
31
+ if (!capability) {
32
+ throw new Error(\`Capability "\${type}/\${key}" not found\`);
33
+ }
34
+
35
+ return capability.handler(context, options);
36
+ }
37
+ `;
38
+ }
39
+ export {
40
+ generateEntry
41
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=codegen.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen.test.d.ts","sourceRoot":"","sources":["../../src/cli/codegen.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import type { CapabilityType } from "../manifest.js";
2
+ /**
3
+ * A capability type the build tool knows how to discover: which directory
4
+ * under `src/` its files live in, and the `_tag` its default exports must
5
+ * carry. This SDK intentionally registers workflows only.
6
+ */
7
+ export type CapabilityTypeDefinition = {
8
+ type: CapabilityType;
9
+ directory: string;
10
+ tag: string;
11
+ };
12
+ export declare const CAPABILITY_TYPES: readonly CapabilityTypeDefinition[];
13
+ /**
14
+ * A capability file found during discovery. The key is the file's basename;
15
+ * combined with the type it forms the capability's identity (`workflow/onPageCreated`).
16
+ */
17
+ export type DiscoveredCapability = {
18
+ type: CapabilityType;
19
+ tag: string;
20
+ key: string;
21
+ /** Path to the source file, relative to the project root. */
22
+ sourcePath: string;
23
+ };
24
+ /**
25
+ * Discover capability files by convention: every `.ts` file directly under
26
+ * `src/<directory>/` for each registered capability type. Subdirectories are
27
+ * not searched — they hold plain modules, not capabilities.
28
+ *
29
+ * Only workflow files directly under `src/workflows/` are discovered.
30
+ */
31
+ export declare function discoverCapabilities(projectRoot: string): Promise<DiscoveredCapability[]>;
32
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/cli/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,SAAS,wBAAwB,EAE/D,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAiC/F"}
@@ -0,0 +1,37 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ const CAPABILITY_TYPES = [
4
+ { type: "workflow", directory: "workflows", tag: "workflow" }
5
+ ];
6
+ async function discoverCapabilities(projectRoot) {
7
+ const discovered = [];
8
+ for (const definition of CAPABILITY_TYPES) {
9
+ const directory = path.join(projectRoot, "src", definition.directory);
10
+ let entries;
11
+ try {
12
+ entries = await fs.promises.readdir(directory, { withFileTypes: true });
13
+ } catch (error) {
14
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
15
+ continue;
16
+ }
17
+ throw error;
18
+ }
19
+ entries.sort((a, b) => a.name.localeCompare(b.name));
20
+ for (const entry of entries) {
21
+ if (!entry.isFile() || !entry.name.endsWith(".ts") || entry.name.endsWith(".d.ts")) {
22
+ continue;
23
+ }
24
+ discovered.push({
25
+ type: definition.type,
26
+ tag: definition.tag,
27
+ key: entry.name.slice(0, -".ts".length),
28
+ sourcePath: path.join("src", definition.directory, entry.name)
29
+ });
30
+ }
31
+ }
32
+ return discovered;
33
+ }
34
+ export {
35
+ CAPABILITY_TYPES,
36
+ discoverCapabilities
37
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=discover.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.test.d.ts","sourceRoot":"","sources":["../../src/cli/discover.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { AppManifest } from "../manifest.js";
2
+ import type { DiscoveredCapability } from "./discover.js";
3
+ /** Extract the workflow-only app manifest from a built bundle. */
4
+ export declare function extractManifest(bundlePath: string, capabilities: readonly DiscoveredCapability[]): Promise<AppManifest>;
5
+ /**
6
+ * Throw if `value` contains anything `JSON.stringify` would silently drop or
7
+ * rewrite, so invalid workflow configuration fails during the build.
8
+ */
9
+ export declare function assertJsonSafe(value: unknown, path: string): void;
10
+ //# sourceMappingURL=emit-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit-manifest.d.ts","sourceRoot":"","sources":["../../src/cli/emit-manifest.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAgD1D,kEAAkE;AAClE,wBAAsB,eAAe,CACpC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,SAAS,oBAAoB,EAAE,GAC3C,OAAO,CAAC,WAAW,CAAC,CAmCtB;AAgDD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA4CjE"}
@@ -0,0 +1,131 @@
1
+ import { createRequire } from "node:module";
2
+ import { pathToFileURL } from "node:url";
3
+ import { Ajv } from "ajv";
4
+ const SDK_VERSION = (() => {
5
+ const require2 = createRequire(import.meta.url);
6
+ const packageJson = require2("../../package.json");
7
+ if (typeof packageJson !== "object" || packageJson === null || !("version" in packageJson) || typeof packageJson.version !== "string") {
8
+ throw new Error("Failed to read SDK version from package.json");
9
+ }
10
+ return packageJson.version;
11
+ })();
12
+ const MANIFEST_SCHEMA = {
13
+ type: "object",
14
+ properties: {
15
+ sdkVersion: { type: "string" },
16
+ databases: { type: "array", maxItems: 0 },
17
+ xldbs: { type: "array", maxItems: 0 },
18
+ pacers: { type: "array", maxItems: 0 },
19
+ capabilities: {
20
+ type: "array",
21
+ items: {
22
+ type: "object",
23
+ properties: {
24
+ type: { type: "string", const: "workflow" },
25
+ key: { type: "string" },
26
+ config: {}
27
+ },
28
+ required: ["type", "key", "config"],
29
+ additionalProperties: false
30
+ }
31
+ }
32
+ },
33
+ required: ["sdkVersion", "databases", "xldbs", "pacers", "capabilities"],
34
+ additionalProperties: false
35
+ };
36
+ async function extractManifest(bundlePath, capabilities) {
37
+ const bundle = await importBundle(bundlePath);
38
+ const capabilityRegistry = getCapabilityRegistry(bundle);
39
+ const capabilityEntries = capabilities.map((capability) => {
40
+ const value = capabilityRegistry.workflow?.[capability.key];
41
+ const workflow = asWorkflow(value);
42
+ if (!workflow) {
43
+ throw new Error(
44
+ `${capability.sourcePath}: default export is not a workflow \u2014 files under src/workflows/ must default-export createWorkflow(...)`
45
+ );
46
+ }
47
+ assertJsonSafe(workflow.config, `${capability.sourcePath} config`);
48
+ return { type: "workflow", key: capability.key, config: workflow.config };
49
+ });
50
+ const manifest = JSON.parse(
51
+ JSON.stringify({
52
+ sdkVersion: SDK_VERSION,
53
+ databases: [],
54
+ xldbs: [],
55
+ pacers: [],
56
+ capabilities: capabilityEntries
57
+ })
58
+ );
59
+ const ajv = new Ajv();
60
+ if (!ajv.validate(MANIFEST_SCHEMA, manifest)) {
61
+ throw new Error(`Generated manifest is invalid: ${ajv.errorsText(ajv.errors)}`);
62
+ }
63
+ return manifest;
64
+ }
65
+ let importNonce = 0;
66
+ async function importBundle(bundlePath) {
67
+ try {
68
+ return await import(`${pathToFileURL(bundlePath).href}?import=${importNonce++}`);
69
+ } catch (error) {
70
+ throw new Error(
71
+ `Failed to import built app bundle. Workflow modules must be importable without secrets or network access \u2014 check for top-level code that throws.
72
+ Caused by: ${error instanceof Error ? error.message : String(error)}`
73
+ );
74
+ }
75
+ }
76
+ function getCapabilityRegistry(bundle) {
77
+ if (typeof bundle !== "object" || bundle === null || !("capabilities" in bundle) || typeof bundle.capabilities !== "object" || bundle.capabilities === null) {
78
+ throw new Error("Built app bundle does not export a capability registry");
79
+ }
80
+ return bundle.capabilities;
81
+ }
82
+ function asWorkflow(value) {
83
+ if (typeof value === "object" && value !== null && "_tag" in value && value._tag === "workflow" && "config" in value && typeof value.config === "object" && value.config !== null) {
84
+ return value;
85
+ }
86
+ return void 0;
87
+ }
88
+ function assertJsonSafe(value, path) {
89
+ const reject = (reason) => {
90
+ throw new Error(`${path}: not JSON-serializable (${reason})`);
91
+ };
92
+ switch (typeof value) {
93
+ case "function":
94
+ reject("function");
95
+ break;
96
+ case "symbol":
97
+ reject("symbol");
98
+ break;
99
+ case "bigint":
100
+ reject("bigint");
101
+ break;
102
+ case "number":
103
+ if (!Number.isFinite(value)) reject(String(value));
104
+ return;
105
+ case "string":
106
+ case "boolean":
107
+ case "undefined":
108
+ return;
109
+ }
110
+ if (value === null) return;
111
+ if (Array.isArray(value)) {
112
+ value.forEach((element, index) => {
113
+ if (element === void 0) reject(`undefined at [${index}]`);
114
+ assertJsonSafe(element, `${path}[${index}]`);
115
+ });
116
+ return;
117
+ }
118
+ const object = value;
119
+ const prototype = Object.getPrototypeOf(object);
120
+ if (prototype !== null && prototype !== Object.prototype) {
121
+ reject(object.constructor?.name ?? "non-plain object");
122
+ }
123
+ for (const [key, property] of Object.entries(object)) {
124
+ if (property === void 0) continue;
125
+ assertJsonSafe(property, `${path}.${key}`);
126
+ }
127
+ }
128
+ export {
129
+ assertJsonSafe,
130
+ extractManifest
131
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=emit-manifest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit-manifest.test.d.ts","sourceRoot":"","sources":["../../src/cli/emit-manifest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import * as path from "node:path";
3
+ import { buildApp } from "./build.js";
4
+ const USAGE = `Usage: notion-apps <command>
5
+
6
+ Commands:
7
+ build Build the app in the current directory
8
+ `;
9
+ async function main() {
10
+ const command = process.argv[2];
11
+ switch (command) {
12
+ case "build": {
13
+ const projectRoot = process.cwd();
14
+ const { manifest, bundlePath, manifestPath } = await buildApp(projectRoot);
15
+ const count = manifest.capabilities.length;
16
+ console.log(
17
+ `Built ${path.relative(projectRoot, bundlePath)} and ${path.relative(projectRoot, manifestPath)} (${count} ${count === 1 ? "capability" : "capabilities"})`
18
+ );
19
+ break;
20
+ }
21
+ case void 0:
22
+ case "help":
23
+ case "--help":
24
+ console.log(USAGE);
25
+ break;
26
+ default:
27
+ console.error(`Unknown command: ${command}
28
+
29
+ ${USAGE}`);
30
+ process.exitCode = 1;
31
+ }
32
+ }
33
+ main().catch((error) => {
34
+ console.error(error instanceof Error ? error.message : String(error));
35
+ process.exitCode = 1;
36
+ });
@@ -0,0 +1,7 @@
1
+ import { Client } from "@notionhq/client";
2
+ export type CapabilityContext = {
3
+ /** Notion API SDK client for this execution. */
4
+ notion: Client;
5
+ };
6
+ export declare function createCapabilityContext(): CapabilityContext;
7
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,MAAM,iBAAiB,GAAG;IAC/B,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AA2BF,wBAAgB,uBAAuB,IAAI,iBAAiB,CAe3D"}
@@ -0,0 +1,30 @@
1
+ import { Client } from "@notionhq/client";
2
+ const FIX_STEPS = "\n\nTo fix this:\n1. Create a personal access token at https://app.notion.com/developers/tokens\n2. Add NOTION_API_TOKEN=<your-token> to your .env file\n3. For deployed apps, run: ntn apps env push";
3
+ function missingTokenMessage() {
4
+ return "NOTION_API_TOKEN is not set. context.notion requires an API token to make requests in workflow capabilities to Notion." + FIX_STEPS;
5
+ }
6
+ function createUnauthenticatedNotionProxy() {
7
+ return new Proxy({}, {
8
+ get(_target, prop) {
9
+ if (prop === "then" || typeof prop === "symbol") {
10
+ return void 0;
11
+ }
12
+ throw new Error(missingTokenMessage());
13
+ }
14
+ });
15
+ }
16
+ function createCapabilityContext() {
17
+ const options = {};
18
+ if (process.env.NOTION_API_BASE_URL) {
19
+ options.baseUrl = process.env.NOTION_API_BASE_URL;
20
+ }
21
+ if (!process.env.NOTION_API_TOKEN) {
22
+ return { notion: createUnauthenticatedNotionProxy() };
23
+ }
24
+ options.auth = process.env.NOTION_API_TOKEN;
25
+ const notion = new Client(options);
26
+ return { notion };
27
+ }
28
+ export {
29
+ createCapabilityContext
30
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * An error that occurred during the execution of an app capability.
3
+ */
4
+ export declare class ExecutionError extends Error {
5
+ readonly cause: unknown;
6
+ constructor(cause: unknown);
7
+ }
8
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,SAAkB,KAAK,EAAE,OAAO,CAAC;gBAErB,KAAK,EAAE,OAAO;CAK1B"}
package/dist/error.js ADDED
@@ -0,0 +1,11 @@
1
+ class ExecutionError extends Error {
2
+ cause;
3
+ constructor(cause) {
4
+ super(`Error during app execution: ${cause}`);
5
+ this.name = "ExecutionError";
6
+ this.cause = cause;
7
+ }
8
+ }
9
+ export {
10
+ ExecutionError
11
+ };
@@ -0,0 +1,25 @@
1
+ /** The only capability type supported by this SDK. */
2
+ export type CapabilityType = "workflow";
3
+ /**
4
+ * One capability in the manifest: its type, its key (derived from its
5
+ * filename), and its handler-free configuration.
6
+ */
7
+ export type CapabilityManifestEntry = {
8
+ type: CapabilityType;
9
+ key: string;
10
+ config: unknown;
11
+ };
12
+ /**
13
+ * The full app manifest, emitted by `notion-apps build` as
14
+ * `dist/manifest.json` and read by the platform at deploy time. This is a
15
+ * complete, static description of the app.
16
+ */
17
+ export type AppManifest = {
18
+ sdkVersion: string;
19
+ /** Reserved platform fields; workflow-only apps always emit empty arrays. */
20
+ databases: never[];
21
+ xldbs: never[];
22
+ pacers: never[];
23
+ capabilities: CapabilityManifestEntry[];
24
+ };
25
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC;AAExC;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACrC,IAAI,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,SAAS,EAAE,KAAK,EAAE,CAAC;IACnB,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,YAAY,EAAE,uBAAuB,EAAE,CAAC;CACxC,CAAC"}
File without changes
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Writes a notion_output envelope to stdout using the shared wire format.
3
+ * The platform runner scans a run's log stream for this envelope to
4
+ * recover the capability's result.
5
+ */
6
+ export declare function writeOutput(value: unknown): void;
7
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEhD"}
package/dist/output.js ADDED
@@ -0,0 +1,8 @@
1
+ function writeOutput(value) {
2
+ process.stdout.write(`
3
+ <__notion_output__>${JSON.stringify(value)}</__notion_output__>
4
+ `);
5
+ }
6
+ export {
7
+ writeOutput
8
+ };
@@ -0,0 +1,9 @@
1
+ export type RunMetadata = {
2
+ workspaceId: string;
3
+ workerId: string;
4
+ runGroupId: string;
5
+ runId: string;
6
+ capabilityType: "workflow";
7
+ attemptNumber: number;
8
+ };
9
+ //# sourceMappingURL=runtime-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-metadata.d.ts","sourceRoot":"","sources":["../src/runtime-metadata.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACtB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { Ajv } from "ajv";
3
+ const runMetadataSchema = {
4
+ type: "object",
5
+ properties: {
6
+ workspaceId: { type: "string" },
7
+ workerId: { type: "string" },
8
+ runGroupId: { type: "string" },
9
+ runId: { type: "string" },
10
+ capabilityType: { type: "string", const: "workflow" },
11
+ attemptNumber: { type: "number" }
12
+ },
13
+ required: ["workspaceId", "workerId", "runGroupId", "runId", "capabilityType", "attemptNumber"],
14
+ additionalProperties: false
15
+ };
16
+ const ajv = new Ajv();
17
+ const validateRunMetadata = ajv.compile(runMetadataSchema);
18
+ function readRunMetadata() {
19
+ const runMetadataPath = process.env.NOTION_RUN_METADATA_PATH;
20
+ if (runMetadataPath === void 0) {
21
+ throw new Error("Workflow run metadata path is not configured");
22
+ }
23
+ const parsed = JSON.parse(readFileSync(runMetadataPath, "utf8"));
24
+ if (!validateRunMetadata(parsed)) {
25
+ throw new Error(
26
+ `Invalid workflow run metadata: ${ajv.errorsText(validateRunMetadata.errors)}`
27
+ );
28
+ }
29
+ return parsed;
30
+ }
31
+ export {
32
+ readRunMetadata
33
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime-metadata.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-metadata.test.d.ts","sourceRoot":"","sources":["../src/runtime-metadata.test.ts"],"names":[],"mappings":""}