zitejs 0.9.120 → 0.9.122
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/dist/cjs/auth/config.d.ts +3 -0
- package/dist/cjs/auth/config.js +7 -0
- package/dist/cjs/auth/index.js +30 -4
- package/dist/cjs/auth/useAuth.test.js +5 -19
- package/dist/cjs/backend/index.d.ts +30 -3
- package/dist/cjs/backend/index.js +2 -2
- package/dist/cjs/bundle/index.d.ts +1 -1
- package/dist/cjs/bundle/index.js +6 -2
- package/dist/cjs/check/index.js +5 -13
- package/dist/cjs/cli.js +2 -2
- package/dist/cjs/dev/index.js +22 -29
- package/dist/cjs/sourceRoots.d.ts +19 -0
- package/dist/cjs/sourceRoots.js +33 -0
- package/dist/cjs/sync/lib.js +9 -7
- package/dist/cjs/sync/lib.test.js +5 -0
- package/dist/cjs/upload/index.js +19 -0
- package/dist/cjs/upload/index.test.js +30 -3
- package/dist/cjs/vite/domTagNames.d.ts +1 -0
- package/dist/cjs/vite/domTagNames.js +257 -0
- package/dist/cjs/vite/index.js +20 -11
- package/dist/cjs/vite/index.test.d.ts +1 -0
- package/dist/cjs/vite/index.test.js +53 -0
- package/dist/esm/auth/config.d.ts +3 -0
- package/dist/esm/auth/config.js +6 -0
- package/dist/esm/auth/index.js +28 -2
- package/dist/esm/auth/useAuth.test.js +5 -19
- package/dist/esm/backend/index.d.ts +30 -3
- package/dist/esm/backend/index.js +2 -2
- package/dist/esm/bundle/index.d.ts +1 -1
- package/dist/esm/bundle/index.js +6 -2
- package/dist/esm/check/index.js +6 -14
- package/dist/esm/cli.js +2 -2
- package/dist/esm/dev/index.js +22 -29
- package/dist/esm/sourceRoots.d.ts +19 -0
- package/dist/esm/sourceRoots.js +28 -0
- package/dist/esm/sync/lib.js +9 -7
- package/dist/esm/sync/lib.test.js +5 -0
- package/dist/esm/upload/index.js +19 -0
- package/dist/esm/upload/index.test.js +30 -3
- package/dist/esm/vite/domTagNames.d.ts +1 -0
- package/dist/esm/vite/domTagNames.js +254 -0
- package/dist/esm/vite/index.js +20 -11
- package/dist/esm/vite/index.test.d.ts +1 -0
- package/dist/esm/vite/index.test.js +48 -0
- package/package.json +1 -1
package/dist/esm/bundle/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
10
|
* npx zitejs bundle # bundle all endpoints
|
|
11
|
-
* npx zitejs bundle --app admin-panel # bundle endpoints for
|
|
11
|
+
* npx zitejs bundle --app admin-panel # bundle endpoints for one app or automation, by dir name
|
|
12
12
|
* npx zitejs bundle --script <path> # bundle a one-off script
|
|
13
13
|
*
|
|
14
14
|
* Output: JSON to stdout
|
|
@@ -24,6 +24,7 @@ import * as path from 'path';
|
|
|
24
24
|
import * as fs from 'fs';
|
|
25
25
|
import { parse } from '@babel/parser';
|
|
26
26
|
import { builtinModules } from 'node:module';
|
|
27
|
+
import { findSourceDir } from '../sourceRoots.js';
|
|
27
28
|
// workerd's `nodejs_compat` does not provide these. Left external they bundle
|
|
28
29
|
// fine and then kill the worker at load time with `No such module`; excluded,
|
|
29
30
|
// esbuild fails the one endpoint with a resolvable error instead. Pinned by
|
|
@@ -578,7 +579,10 @@ export async function runBundle() {
|
|
|
578
579
|
const appFlag = args.indexOf('--app');
|
|
579
580
|
let baseDir = process.cwd();
|
|
580
581
|
if (appFlag !== -1 && args[appFlag + 1]) {
|
|
581
|
-
|
|
582
|
+
const name = args[appFlag + 1];
|
|
583
|
+
// A dir that exists under no root still resolves under `apps/`, so the
|
|
584
|
+
// error names the path a caller expects rather than "not found".
|
|
585
|
+
baseDir = path.resolve(baseDir, findSourceDir(name, baseDir)?.path ?? path.join('apps', name));
|
|
582
586
|
}
|
|
583
587
|
// If explicit endpoint names passed, use those; otherwise find all.
|
|
584
588
|
// Skipping `--app`'s VALUE as well as the flag: it is a positional arg, so
|
package/dist/esm/check/index.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from 'child_process';
|
|
2
|
-
import { existsSync
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
-
|
|
5
|
-
const appsDir = 'apps';
|
|
6
|
-
if (!existsSync(appsDir))
|
|
7
|
-
return [];
|
|
8
|
-
return readdirSync(appsDir, { withFileTypes: true })
|
|
9
|
-
.filter(d => d.isDirectory())
|
|
10
|
-
.map(d => d.name);
|
|
11
|
-
}
|
|
4
|
+
import { listSourceDirs } from '../sourceRoots.js';
|
|
12
5
|
/**
|
|
13
6
|
* An argument array, not a command string, so no shell is involved.
|
|
14
7
|
*
|
|
@@ -63,15 +56,14 @@ function bundleFailures(output) {
|
|
|
63
56
|
return failures;
|
|
64
57
|
}
|
|
65
58
|
export async function runCheck() {
|
|
66
|
-
const appDirs =
|
|
59
|
+
const appDirs = listSourceDirs();
|
|
67
60
|
if (appDirs.length === 0) {
|
|
68
|
-
console.error('No apps found
|
|
61
|
+
console.error('No apps or automations found under apps/ or automations/.');
|
|
69
62
|
process.exit(1);
|
|
70
63
|
}
|
|
71
64
|
let allPassed = true;
|
|
72
|
-
for (const app of appDirs) {
|
|
73
|
-
|
|
74
|
-
console.log(`\n── ${app} ──`);
|
|
65
|
+
for (const { dir: app, path: appPath } of appDirs) {
|
|
66
|
+
console.log(`\n── ${appPath} ──`);
|
|
75
67
|
// Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
|
|
76
68
|
// solution-style config — `"files": []` plus a reference — so
|
|
77
69
|
// `tsc --noEmit -p tsconfig.json` type-checks zero files and exits 0. It
|
package/dist/esm/cli.js
CHANGED
|
@@ -38,8 +38,8 @@ async function main() {
|
|
|
38
38
|
console.error('Commands:');
|
|
39
39
|
console.error(' sync Generate .zite/db.ts from database schema (single-app)');
|
|
40
40
|
console.error(' dev Run sync then watch for changes (like npx convex dev)');
|
|
41
|
-
console.error(' generate
|
|
42
|
-
console.error(' check Run tsc --noEmit and vite build for
|
|
41
|
+
console.error(' generate Regenerate every app and automation .zite/ (monorepo)');
|
|
42
|
+
console.error(' check Run tsc --noEmit (and vite build where there is one) for every app and automation');
|
|
43
43
|
console.error(' bundle Bundle src/api/*.ts endpoints for cloudflare-lambda');
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
package/dist/esm/dev/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { watch } from "fs";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
+
import { listSourceDirs } from "../sourceRoots.js";
|
|
4
5
|
import { runSync } from "../sync/index.js";
|
|
5
6
|
import { generateDbTs, generateApiTs, generateBackendWrapperTs, generateAirtableTs, findEmailIntegrationId, generateEmailSdk, } from "../sync/lib.js";
|
|
6
7
|
const debounceTimers = new Map();
|
|
@@ -10,17 +11,9 @@ function debounce(key, fn, ms) {
|
|
|
10
11
|
clearTimeout(existing);
|
|
11
12
|
debounceTimers.set(key, setTimeout(fn, ms));
|
|
12
13
|
}
|
|
13
|
-
function findAppDirs() {
|
|
14
|
-
const appsDir = "apps";
|
|
15
|
-
if (!existsSync(appsDir))
|
|
16
|
-
return [];
|
|
17
|
-
return readdirSync(appsDir, { withFileTypes: true })
|
|
18
|
-
.filter((d) => d.isDirectory())
|
|
19
|
-
.map((d) => d.name);
|
|
20
|
-
}
|
|
21
14
|
function getFlowId(appDir) {
|
|
22
15
|
try {
|
|
23
|
-
const configPath = join(
|
|
16
|
+
const configPath = join(appDir.path, "zite.config.json");
|
|
24
17
|
if (existsSync(configPath)) {
|
|
25
18
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
26
19
|
return config.id;
|
|
@@ -40,11 +33,11 @@ const readJsonFile = (path) => {
|
|
|
40
33
|
}
|
|
41
34
|
};
|
|
42
35
|
function getEmailIntegrationId(appDir) {
|
|
43
|
-
return findEmailIntegrationId(readJsonFile(join(
|
|
36
|
+
return findEmailIntegrationId(readJsonFile(join(appDir.path, "zite.config.json")), readJsonFile("zite.config.json"));
|
|
44
37
|
}
|
|
45
38
|
function getDeclaredEnvVarNames(appDir) {
|
|
46
39
|
try {
|
|
47
|
-
const configPath = join(
|
|
40
|
+
const configPath = join(appDir.path, "zite.config.json");
|
|
48
41
|
if (!existsSync(configPath))
|
|
49
42
|
return [];
|
|
50
43
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
@@ -55,7 +48,7 @@ function getDeclaredEnvVarNames(appDir) {
|
|
|
55
48
|
}
|
|
56
49
|
}
|
|
57
50
|
function regenerateAppTypedWrappers(appDir) {
|
|
58
|
-
const outDir = join(
|
|
51
|
+
const outDir = join(appDir.path, ".zite");
|
|
59
52
|
mkdirSync(outDir, { recursive: true });
|
|
60
53
|
// user.ts and auth.ts no longer generated — User type is fixed in zitejs/auth
|
|
61
54
|
// Email integration: generate the Email client at .zite/integrations/email.ts.
|
|
@@ -70,7 +63,7 @@ function regenerateAppTypedWrappers(appDir) {
|
|
|
70
63
|
writeFileSync(join(outDir, "backend.ts"), generateBackendWrapperTs(getDeclaredEnvVarNames(appDir)));
|
|
71
64
|
}
|
|
72
65
|
function regenerateAppApiTs(appDir) {
|
|
73
|
-
const apiDir = join(
|
|
66
|
+
const apiDir = join(appDir.path, "src", "api");
|
|
74
67
|
if (!existsSync(apiDir))
|
|
75
68
|
return;
|
|
76
69
|
// Sorted because `readdir` order is unspecified — it is whatever the
|
|
@@ -90,14 +83,14 @@ function regenerateAppApiTs(appDir) {
|
|
|
90
83
|
}));
|
|
91
84
|
const content = generateApiTs(endpointFiles);
|
|
92
85
|
if (content) {
|
|
93
|
-
const outDir = join(
|
|
86
|
+
const outDir = join(appDir.path, ".zite");
|
|
94
87
|
mkdirSync(outDir, { recursive: true });
|
|
95
88
|
writeFileSync(join(outDir, "api.ts"), content);
|
|
96
|
-
console.log(`Updated
|
|
89
|
+
console.log(`Updated ${appDir.path}/.zite/api.ts`);
|
|
97
90
|
}
|
|
98
91
|
}
|
|
99
92
|
function regenerateAppAirtableSdk(appDir) {
|
|
100
|
-
const lockPath = join(
|
|
93
|
+
const lockPath = join(appDir.path, "zite.lock");
|
|
101
94
|
console.log(`[airtable-sdk] Checking ${lockPath} exists: ${existsSync(lockPath)}`);
|
|
102
95
|
if (!existsSync(lockPath))
|
|
103
96
|
return;
|
|
@@ -118,15 +111,15 @@ function regenerateAppAirtableSdk(appDir) {
|
|
|
118
111
|
};
|
|
119
112
|
const content = generateAirtableTs(airtableLock);
|
|
120
113
|
if (content) {
|
|
121
|
-
const outDir = join(
|
|
114
|
+
const outDir = join(appDir.path, ".zite", "integrations");
|
|
122
115
|
mkdirSync(outDir, { recursive: true });
|
|
123
116
|
writeFileSync(join(outDir, "airtable.ts"), content);
|
|
124
|
-
console.log(`Updated
|
|
117
|
+
console.log(`Updated ${appDir.path}/.zite/integrations/airtable.ts`);
|
|
125
118
|
}
|
|
126
119
|
}
|
|
127
120
|
}
|
|
128
121
|
catch (err) {
|
|
129
|
-
console.error(`[airtable-sdk] Error processing lock for ${appDir}:`, err);
|
|
122
|
+
console.error(`[airtable-sdk] Error processing lock for ${appDir.path}:`, err);
|
|
130
123
|
}
|
|
131
124
|
}
|
|
132
125
|
export async function runGenerate() {
|
|
@@ -150,8 +143,8 @@ export async function runGenerate() {
|
|
|
150
143
|
catch (err) {
|
|
151
144
|
console.warn("DB SDK generation failed:", err instanceof Error ? err.message : err);
|
|
152
145
|
}
|
|
153
|
-
// 2. Find
|
|
154
|
-
const appDirs =
|
|
146
|
+
// 2. Find every app and automation and regenerate their .zite/ files
|
|
147
|
+
const appDirs = listSourceDirs();
|
|
155
148
|
for (const app of appDirs) {
|
|
156
149
|
regenerateAppApiTs(app);
|
|
157
150
|
regenerateAppTypedWrappers(app);
|
|
@@ -171,33 +164,33 @@ export async function runDev() {
|
|
|
171
164
|
console.warn("Initial sync failed (continuing with watcher):", err instanceof Error ? err.message : err);
|
|
172
165
|
}
|
|
173
166
|
console.log("");
|
|
174
|
-
// 2. Find
|
|
175
|
-
const appDirs =
|
|
167
|
+
// 2. Find every app and automation and regenerate their .zite/ files
|
|
168
|
+
const appDirs = listSourceDirs();
|
|
176
169
|
for (const app of appDirs) {
|
|
177
170
|
regenerateAppApiTs(app);
|
|
178
171
|
regenerateAppTypedWrappers(app);
|
|
179
172
|
}
|
|
180
|
-
// 3. Watch each
|
|
173
|
+
// 3. Watch each dir's src/api/ for endpoint changes
|
|
181
174
|
let watchingAny = false;
|
|
182
175
|
for (const app of appDirs) {
|
|
183
|
-
const apiDir = join(
|
|
176
|
+
const apiDir = join(app.path, "src", "api");
|
|
184
177
|
if (!existsSync(apiDir))
|
|
185
178
|
continue;
|
|
186
179
|
watchingAny = true;
|
|
187
|
-
console.log(`Watching
|
|
180
|
+
console.log(`Watching ${app.path}/src/api/ for endpoint changes...`);
|
|
188
181
|
watch(apiDir, { recursive: true }, (_event, filename) => {
|
|
189
182
|
if (!filename)
|
|
190
183
|
return;
|
|
191
184
|
if (!filename.endsWith(".ts") && !filename.endsWith(".js"))
|
|
192
185
|
return;
|
|
193
|
-
debounce(app, () => {
|
|
194
|
-
console.log(`Endpoint changed in ${app}: ${filename}`);
|
|
186
|
+
debounce(app.path, () => {
|
|
187
|
+
console.log(`Endpoint changed in ${app.path}: ${filename}`);
|
|
195
188
|
regenerateAppApiTs(app);
|
|
196
189
|
}, 200);
|
|
197
190
|
});
|
|
198
191
|
}
|
|
199
192
|
if (!watchingAny) {
|
|
200
|
-
console.log("No apps with src/api/ found.");
|
|
193
|
+
console.log("No apps or automations with src/api/ found.");
|
|
201
194
|
}
|
|
202
195
|
// 4. Watch zite.schema.json for schema drift
|
|
203
196
|
if (existsSync("zite.schema.json")) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The repo-root directories a project's own source lives under: `apps/` for
|
|
3
|
+
* apps and `automations/` for automations (endpoints with no frontend). The
|
|
4
|
+
* backend keeps the same list; a dir name is unique across both roots, so a
|
|
5
|
+
* bare name still identifies one dir.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SOURCE_ROOTS: readonly ["apps", "automations"];
|
|
8
|
+
export type SourceRoot = (typeof SOURCE_ROOTS)[number];
|
|
9
|
+
export interface SourceDir {
|
|
10
|
+
root: SourceRoot;
|
|
11
|
+
/** The bare directory name. */
|
|
12
|
+
dir: string;
|
|
13
|
+
/** `<root>/<dir>`, relative to the repo root. */
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
/** Every source dir under every root, in root order then name order. */
|
|
17
|
+
export declare function listSourceDirs(cwd?: string): SourceDir[];
|
|
18
|
+
/** The source dir with this bare name, under whichever root holds it. */
|
|
19
|
+
export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* The repo-root directories a project's own source lives under: `apps/` for
|
|
5
|
+
* apps and `automations/` for automations (endpoints with no frontend). The
|
|
6
|
+
* backend keeps the same list; a dir name is unique across both roots, so a
|
|
7
|
+
* bare name still identifies one dir.
|
|
8
|
+
*/
|
|
9
|
+
export const SOURCE_ROOTS = ["apps", "automations"];
|
|
10
|
+
/** Every source dir under every root, in root order then name order. */
|
|
11
|
+
export function listSourceDirs(cwd = ".") {
|
|
12
|
+
const dirs = [];
|
|
13
|
+
for (const root of SOURCE_ROOTS) {
|
|
14
|
+
const rootPath = join(cwd, root);
|
|
15
|
+
if (!existsSync(rootPath))
|
|
16
|
+
continue;
|
|
17
|
+
for (const entry of readdirSync(rootPath, { withFileTypes: true })) {
|
|
18
|
+
if (!entry.isDirectory())
|
|
19
|
+
continue;
|
|
20
|
+
dirs.push({ root, dir: entry.name, path: join(root, entry.name) });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return dirs;
|
|
24
|
+
}
|
|
25
|
+
/** The source dir with this bare name, under whichever root holds it. */
|
|
26
|
+
export function findSourceDir(name, cwd = ".") {
|
|
27
|
+
return listSourceDirs(cwd).find((d) => d.dir === name);
|
|
28
|
+
}
|
package/dist/esm/sync/lib.js
CHANGED
|
@@ -1216,10 +1216,10 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1216
1216
|
"// Auto-generated type-narrowing wrapper. Do not edit manually.",
|
|
1217
1217
|
"// Re-exports createEndpoint with context.user typed to the app User.",
|
|
1218
1218
|
"",
|
|
1219
|
-
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStreamInterface, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1219
|
+
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStreamInterface, ZiteTrigger, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1220
1220
|
"import type { User } from 'zitejs/auth';",
|
|
1221
1221
|
"",
|
|
1222
|
-
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStreamInterface, ZiteWebhook };",
|
|
1222
|
+
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
|
|
1223
1223
|
// The pre-monorepo SDK put this in scope for every endpoint, so migrated
|
|
1224
1224
|
// code can name it. `createEndpoint` infers the same thing without it.
|
|
1225
1225
|
"export type InferSchemaType<T> = T extends { _output: infer U } ? U : T;",
|
|
@@ -1294,7 +1294,7 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1294
1294
|
// TStream mirrors zitejs/backend/base. Without it `stream: true` endpoints
|
|
1295
1295
|
// get no `stream` argument here — and this wrapper, not the base module, is
|
|
1296
1296
|
// what `zitejs/backend` resolves to in every app.
|
|
1297
|
-
"export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream extends boolean = false, TSchedule extends ZiteSchedule | undefined = undefined, TWebhook extends ZiteWebhook | undefined = undefined, TRawInput = TInput> {",
|
|
1297
|
+
"export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream extends boolean = false, TSchedule extends ZiteSchedule | undefined = undefined, TWebhook extends ZiteWebhook | undefined = undefined, TRawInput = TInput, TTrigger extends ZiteTrigger | undefined = undefined> {",
|
|
1298
1298
|
" description?: string;",
|
|
1299
1299
|
" inputSchema?: SchemaLike<TInput, TRawInput>;",
|
|
1300
1300
|
// Apps compile against this copy, not the base module's.
|
|
@@ -1306,17 +1306,19 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1306
1306
|
" schedule?: TSchedule;",
|
|
1307
1307
|
" /** When set, an inbound webhook can also trigger this endpoint. Like `schedule`, it widens `context` — a webhook fire has no session. */",
|
|
1308
1308
|
" webhook?: TWebhook;",
|
|
1309
|
+
" /** When set, a workspace connection's events (registered by `src/triggers/<endpointId>/subscribe.ts`) or a poll also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
|
|
1310
|
+
" trigger?: TTrigger;",
|
|
1309
1311
|
" execute: (",
|
|
1310
1312
|
" params: {",
|
|
1311
1313
|
" input: TInput;",
|
|
1312
|
-
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|
|
1314
|
+
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|
|
1313
1315
|
" } & (TStream extends true ? { stream: ZiteStreamInterface } : {}),",
|
|
1314
1316
|
" ) => Promise<TOutput> | TOutput;",
|
|
1315
1317
|
"}",
|
|
1316
1318
|
"",
|
|
1317
|
-
"export function createEndpoint<TInput = unknown, TOutput = unknown, TStream extends boolean = false, TSchedule extends ZiteSchedule | undefined = undefined, TWebhook extends ZiteWebhook | undefined = undefined, TRawInput = TInput>(",
|
|
1318
|
-
" config: EndpointConfig<TInput, TOutput, TStream, TSchedule, TWebhook, TRawInput>,",
|
|
1319
|
-
"): EndpointConfig<TInput, TOutput, TStream, TSchedule, TWebhook, TRawInput> {",
|
|
1319
|
+
"export function createEndpoint<TInput = unknown, TOutput = unknown, TStream extends boolean = false, TSchedule extends ZiteSchedule | undefined = undefined, TWebhook extends ZiteWebhook | undefined = undefined, TRawInput = TInput, TTrigger extends ZiteTrigger | undefined = undefined>(",
|
|
1320
|
+
" config: EndpointConfig<TInput, TOutput, TStream, TSchedule, TWebhook, TRawInput, TTrigger>,",
|
|
1321
|
+
"): EndpointConfig<TInput, TOutput, TStream, TSchedule, TWebhook, TRawInput, TTrigger> {",
|
|
1320
1322
|
" return config;",
|
|
1321
1323
|
"}",
|
|
1322
1324
|
"",
|
|
@@ -376,6 +376,11 @@ describe('generateBackendWrapperTs', () => {
|
|
|
376
376
|
it('exports createEndpoint', () => {
|
|
377
377
|
expect(output).toContain('createEndpoint');
|
|
378
378
|
});
|
|
379
|
+
it('types the trigger field like schedule and webhook', () => {
|
|
380
|
+
expect(output).toContain('trigger?: TTrigger;');
|
|
381
|
+
expect(output).toContain('TTrigger extends ZiteTrigger | undefined = undefined');
|
|
382
|
+
expect(output).toContain('ZiteTrigger, ZiteWebhook }');
|
|
383
|
+
});
|
|
379
384
|
it('exports ZiteError', () => {
|
|
380
385
|
expect(output).toContain('ZiteError');
|
|
381
386
|
});
|
package/dist/esm/upload/index.js
CHANGED
|
@@ -106,6 +106,25 @@ async function performUpload(data, filename) {
|
|
|
106
106
|
if (!putRes.ok) {
|
|
107
107
|
throw new FileUploadError('Upload failed');
|
|
108
108
|
}
|
|
109
|
+
// Gateways that predate the storage ledger omit the token. The file is
|
|
110
|
+
// already at fileUrl, so those uploads still succeed.
|
|
111
|
+
if (session.completionToken) {
|
|
112
|
+
const completeRes = await fetch(getApiUrl() +
|
|
113
|
+
'/v1/zite/public/' +
|
|
114
|
+
flowId +
|
|
115
|
+
'/complete-upload?mode=' +
|
|
116
|
+
mode, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
...authHeaders(),
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({ completionToken: session.completionToken }),
|
|
123
|
+
});
|
|
124
|
+
if (!completeRes.ok) {
|
|
125
|
+
throw new FileUploadError(await readErrorMessage(completeRes, 'Upload failed'));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
109
128
|
return session.fileUrl;
|
|
110
129
|
}
|
|
111
130
|
export async function uploadFile({ data, filename, }) {
|
|
@@ -23,7 +23,7 @@ describe('toUploadBlob', () => {
|
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
describe('uploadFile', () => {
|
|
26
|
-
it('requests a session
|
|
26
|
+
it('requests a session, PUTs the bytes, then completes the upload', async () => {
|
|
27
27
|
const file = new File(['hello-world'], 'hello.txt', { type: 'text/plain' });
|
|
28
28
|
fetchMock
|
|
29
29
|
.mockResolvedValueOnce({
|
|
@@ -33,12 +33,14 @@ describe('uploadFile', () => {
|
|
|
33
33
|
presignedUrl: 'https://s3.example.com/put',
|
|
34
34
|
fileUrl: 'https://uploads.zite.com/orgid-1/zite-uploads/app_123/hello.txt',
|
|
35
35
|
contentType: 'text/plain',
|
|
36
|
+
completionToken: 'signed-token',
|
|
36
37
|
}),
|
|
37
38
|
})
|
|
38
|
-
.mockResolvedValueOnce({ ok: true })
|
|
39
|
+
.mockResolvedValueOnce({ ok: true })
|
|
40
|
+
.mockResolvedValueOnce({ ok: true, json: async () => ({ success: true }) });
|
|
39
41
|
const result = await uploadFile({ data: file, filename: 'hello.txt' });
|
|
40
42
|
expect(result.fileUrl).toContain('hello.txt');
|
|
41
|
-
expect(fetchMock).toHaveBeenCalledTimes(
|
|
43
|
+
expect(fetchMock).toHaveBeenCalledTimes(3);
|
|
42
44
|
expect(fetchMock.mock.calls[0][0]).toBe('https://api.example.com/v1/zite/public/app_123/upload-session?mode=live');
|
|
43
45
|
const sessionInit = fetchMock.mock.calls[0][1];
|
|
44
46
|
expect(JSON.parse(sessionInit.body)).toEqual({
|
|
@@ -48,6 +50,31 @@ describe('uploadFile', () => {
|
|
|
48
50
|
});
|
|
49
51
|
expect(fetchMock.mock.calls[1][0]).toBe('https://s3.example.com/put');
|
|
50
52
|
expect(fetchMock.mock.calls[1][1].method).toBe('PUT');
|
|
53
|
+
expect(fetchMock.mock.calls[2][0]).toBe('https://api.example.com/v1/zite/public/app_123/complete-upload?mode=live');
|
|
54
|
+
const completeInit = fetchMock.mock.calls[2][1];
|
|
55
|
+
expect(completeInit.method).toBe('POST');
|
|
56
|
+
expect(JSON.parse(completeInit.body)).toEqual({
|
|
57
|
+
completionToken: 'signed-token',
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it('skips completion when the gateway does not issue a token', async () => {
|
|
61
|
+
fetchMock
|
|
62
|
+
.mockResolvedValueOnce({
|
|
63
|
+
ok: true,
|
|
64
|
+
json: async () => ({
|
|
65
|
+
success: true,
|
|
66
|
+
presignedUrl: 'https://s3.example.com/put',
|
|
67
|
+
fileUrl: 'https://uploads.zite.com/orgid-1/zite-uploads/app_123/hello.txt',
|
|
68
|
+
contentType: 'text/plain',
|
|
69
|
+
}),
|
|
70
|
+
})
|
|
71
|
+
.mockResolvedValueOnce({ ok: true });
|
|
72
|
+
const result = await uploadFile({
|
|
73
|
+
data: new File(['hello-world'], 'hello.txt', { type: 'text/plain' }),
|
|
74
|
+
filename: 'hello.txt',
|
|
75
|
+
});
|
|
76
|
+
expect(result.fileUrl).toContain('hello.txt');
|
|
77
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
51
78
|
});
|
|
52
79
|
it('surfaces the plan-limit message from the session endpoint', async () => {
|
|
53
80
|
fetchMock.mockResolvedValueOnce({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const domTagNames: ReadonlySet<string>;
|