zitejs 0.9.123 → 0.9.124
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 +4 -0
- package/dist/cjs/auth/index.d.ts +2 -2
- package/dist/cjs/bundle/index.d.ts +4 -0
- package/dist/cjs/bundle/index.js +28 -9
- package/dist/cjs/dev/index.d.ts +5 -0
- package/dist/cjs/dev/index.js +46 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +7 -1
- package/dist/cjs/integrations/client.d.ts +25 -0
- package/dist/cjs/integrations/client.js +21 -0
- package/dist/cjs/integrations/client.test-d.d.ts +1 -0
- package/dist/cjs/integrations/client.test-d.js +50 -0
- package/dist/cjs/integrations/index.d.ts +3 -0
- package/dist/cjs/integrations/index.js +6 -0
- package/dist/cjs/integrations/rules.d.ts +67 -0
- package/dist/cjs/integrations/rules.js +104 -0
- package/dist/cjs/integrations/rules.test.d.ts +1 -0
- package/dist/cjs/integrations/rules.test.js +86 -0
- package/dist/cjs/internal/sdkCall.d.ts +1 -0
- package/dist/cjs/internal/sdkCall.js +3 -1
- package/dist/cjs/runtime/index.d.ts +1 -0
- package/dist/cjs/runtime/index.js +3 -1
- package/dist/cjs/sync/lib.d.ts +9 -0
- package/dist/cjs/sync/lib.js +53 -0
- package/dist/cjs/sync/lib.test.js +119 -0
- package/dist/esm/auth/index.d.ts +2 -2
- package/dist/esm/bundle/index.d.ts +4 -0
- package/dist/esm/bundle/index.js +28 -10
- package/dist/esm/dev/index.d.ts +5 -0
- package/dist/esm/dev/index.js +47 -2
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/integrations/client.d.ts +25 -0
- package/dist/esm/integrations/client.js +17 -0
- package/dist/esm/integrations/client.test-d.d.ts +1 -0
- package/dist/esm/integrations/client.test-d.js +48 -0
- package/dist/esm/integrations/index.d.ts +3 -0
- package/dist/esm/integrations/index.js +3 -0
- package/dist/esm/integrations/rules.d.ts +67 -0
- package/dist/esm/integrations/rules.js +98 -0
- package/dist/esm/integrations/rules.test.d.ts +1 -0
- package/dist/esm/integrations/rules.test.js +84 -0
- package/dist/esm/internal/sdkCall.d.ts +1 -0
- package/dist/esm/internal/sdkCall.js +2 -1
- package/dist/esm/runtime/index.d.ts +1 -0
- package/dist/esm/runtime/index.js +1 -0
- package/dist/esm/sync/lib.d.ts +9 -0
- package/dist/esm/sync/lib.js +52 -0
- package/dist/esm/sync/lib.test.js +120 -1
- package/package.json +10 -1
- package/dist/cjs/meta/index.d.ts +0 -14
- package/dist/cjs/meta/index.js +0 -12
- package/dist/cjs/notifications/index.d.ts +0 -25
- package/dist/cjs/notifications/index.js +0 -12
- package/dist/esm/meta/index.d.ts +0 -14
- package/dist/esm/meta/index.js +0 -8
- package/dist/esm/notifications/index.d.ts +0 -25
- package/dist/esm/notifications/index.js +0 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createCaller = void 0;
|
|
3
|
+
exports.createCaller = exports.createIntegrationsClient = void 0;
|
|
4
4
|
exports.createTableClient = createTableClient;
|
|
5
5
|
exports.createSqlClient = createSqlClient;
|
|
6
6
|
exports.createAuthClient = createAuthClient;
|
|
@@ -211,5 +211,7 @@ function createEmailClient(integrationId) {
|
|
|
211
211
|
send: (params) => (0, sdkCall_js_1.getSdkCall)()(integrationId, "Email", "send", params),
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
|
+
var client_js_1 = require("../integrations/client.js");
|
|
215
|
+
Object.defineProperty(exports, "createIntegrationsClient", { enumerable: true, get: function () { return client_js_1.createIntegrationsClient; } });
|
|
214
216
|
var index_js_1 = require("../caller/index.js");
|
|
215
217
|
Object.defineProperty(exports, "createCaller", { enumerable: true, get: function () { return index_js_1.createCaller; } });
|
package/dist/cjs/sync/lib.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PublicFieldDefinition } from "../types/fields.js";
|
|
2
2
|
import type { Database } from "../types/tables.js";
|
|
3
3
|
import { type SdkNameRename } from "./sdkNames.js";
|
|
4
|
+
import { type GettableIntegration } from "../integrations/rules.js";
|
|
4
5
|
export type ZiteSchemaField = {
|
|
5
6
|
id: string;
|
|
6
7
|
sdkName: string;
|
|
@@ -96,3 +97,11 @@ export declare function findEmailIntegrationId(appConfig: unknown, workspaceConf
|
|
|
96
97
|
* connected integration's key.
|
|
97
98
|
*/
|
|
98
99
|
export declare function generateEmailSdk(integrationId: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* `.zite/integrations/index.ts`: `zitejs/integrations` for one source dir. The
|
|
102
|
+
* `integrations` client typed by what the dir can see, plus the generated
|
|
103
|
+
* Airtable clients when it has them.
|
|
104
|
+
*/
|
|
105
|
+
export declare function generateIntegrationsIndex(entries: GettableIntegration[], opts: {
|
|
106
|
+
reexportAirtable: boolean;
|
|
107
|
+
}): string;
|
package/dist/cjs/sync/lib.js
CHANGED
|
@@ -9,8 +9,10 @@ exports.generateAirtableTs = generateAirtableTs;
|
|
|
9
9
|
exports.generateBackendWrapperTs = generateBackendWrapperTs;
|
|
10
10
|
exports.findEmailIntegrationId = findEmailIntegrationId;
|
|
11
11
|
exports.generateEmailSdk = generateEmailSdk;
|
|
12
|
+
exports.generateIntegrationsIndex = generateIntegrationsIndex;
|
|
12
13
|
const parser_1 = require("@babel/parser");
|
|
13
14
|
const sdkNames_js_1 = require("./sdkNames.js");
|
|
15
|
+
const rules_js_1 = require("../integrations/rules.js");
|
|
14
16
|
const AUTH_USERS_TABLE_ID = "zite_user";
|
|
15
17
|
/**
|
|
16
18
|
* What a field's value looks like when a record is READ back.
|
|
@@ -1397,3 +1399,54 @@ function generateEmailSdk(integrationId) {
|
|
|
1397
1399
|
"",
|
|
1398
1400
|
].join("\n");
|
|
1399
1401
|
}
|
|
1402
|
+
const connectedTypeFor = (serviceType) => {
|
|
1403
|
+
const extraLabels = rules_js_1.CREDENTIAL_FIELDS[serviceType].slice(1);
|
|
1404
|
+
return extraLabels.length === 0
|
|
1405
|
+
? "Connected"
|
|
1406
|
+
: `Connected<{ ${extraLabels.map((label) => `${label}: string`).join("; ")} }>`;
|
|
1407
|
+
};
|
|
1408
|
+
/**
|
|
1409
|
+
* `.zite/integrations/index.ts`: `zitejs/integrations` for one source dir. The
|
|
1410
|
+
* `integrations` client typed by what the dir can see, plus the generated
|
|
1411
|
+
* Airtable clients when it has them.
|
|
1412
|
+
*/
|
|
1413
|
+
function generateIntegrationsIndex(entries, opts) {
|
|
1414
|
+
const sorted = [...entries].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1415
|
+
const nameUnion = sorted.length === 0
|
|
1416
|
+
? "never"
|
|
1417
|
+
: sorted.map(({ name }) => tsSingleQuoted(name)).join(" | ");
|
|
1418
|
+
const byName = sorted.length === 0
|
|
1419
|
+
? ["export type IntegrationByName = {};"]
|
|
1420
|
+
: [
|
|
1421
|
+
"export type IntegrationByName = {",
|
|
1422
|
+
...sorted.map(({ name, serviceType }) => ` ${tsSingleQuoted(name)}: ${connectedTypeFor(serviceType)} | NeedsConnect;`),
|
|
1423
|
+
"};",
|
|
1424
|
+
];
|
|
1425
|
+
return [
|
|
1426
|
+
"// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.",
|
|
1427
|
+
"// zitejs/integrations — an integration's credentials by its zite.config.json entry name.",
|
|
1428
|
+
"//",
|
|
1429
|
+
"// Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):",
|
|
1430
|
+
"// import { integrations } from 'zitejs/integrations';",
|
|
1431
|
+
"//",
|
|
1432
|
+
"// const crm = await integrations.get('<entry name>');",
|
|
1433
|
+
"// if (crm.status !== 'connected') { /* crm.connectUrl is where a person reconnects */ }",
|
|
1434
|
+
"//",
|
|
1435
|
+
"// Call get where the value is used, not once at module scope: a token refreshed",
|
|
1436
|
+
"// mid-run is what the next call returns.",
|
|
1437
|
+
"",
|
|
1438
|
+
// Not 'zitejs/integrations': apps alias that specifier to this very file.
|
|
1439
|
+
"import { createIntegrationsClient } from 'zitejs/runtime';",
|
|
1440
|
+
"import type { Connected, NeedsConnect } from 'zitejs/runtime';",
|
|
1441
|
+
"",
|
|
1442
|
+
...(opts.reexportAirtable ? ["export * from './airtable';", ""] : []),
|
|
1443
|
+
"export type { Connected, NeedsConnect };",
|
|
1444
|
+
"",
|
|
1445
|
+
`export type IntegrationName = ${nameUnion};`,
|
|
1446
|
+
"",
|
|
1447
|
+
...byName,
|
|
1448
|
+
"",
|
|
1449
|
+
"export const integrations = createIntegrationsClient<IntegrationByName>();",
|
|
1450
|
+
"",
|
|
1451
|
+
].join("\n");
|
|
1452
|
+
}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const vitest_1 = require("vitest");
|
|
7
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
8
|
+
const path_1 = require("path");
|
|
8
9
|
const lib_js_1 = require("./lib.js");
|
|
9
10
|
/**
|
|
10
11
|
* Syntax errors in the emitted source. `.zite/db.ts` sits at the repo root and
|
|
@@ -418,3 +419,121 @@ const duplicateIdentifierErrorsIn = (source) => {
|
|
|
418
419
|
(0, vitest_1.expect)((0, lib_js_1.findEmailIntegrationId)(undefined, undefined)).toBeUndefined();
|
|
419
420
|
});
|
|
420
421
|
});
|
|
422
|
+
(0, vitest_1.describe)('generateIntegrationsIndex', () => {
|
|
423
|
+
// Not import.meta: this file also compiles to CommonJS. Vitest runs from the repo root.
|
|
424
|
+
const runtimePath = (0, path_1.resolve)('src/runtime/index.ts');
|
|
425
|
+
/** Type errors in `usage` compiled beside the generated file, `zitejs/runtime` mapped to this source. */
|
|
426
|
+
const typeErrorsIn = (generated, usage, airtable) => {
|
|
427
|
+
const files = {
|
|
428
|
+
'/app/.zite/integrations/index.ts': generated,
|
|
429
|
+
...(airtable ? { '/app/.zite/integrations/airtable.ts': airtable } : {}),
|
|
430
|
+
'/app/src/usage.ts': usage,
|
|
431
|
+
};
|
|
432
|
+
const options = {
|
|
433
|
+
strict: true,
|
|
434
|
+
noEmit: true,
|
|
435
|
+
target: typescript_1.default.ScriptTarget.ES2022,
|
|
436
|
+
module: typescript_1.default.ModuleKind.ESNext,
|
|
437
|
+
moduleResolution: typescript_1.default.ModuleResolutionKind.Bundler,
|
|
438
|
+
skipLibCheck: true,
|
|
439
|
+
baseUrl: '/app',
|
|
440
|
+
paths: {
|
|
441
|
+
'zitejs/runtime': [runtimePath],
|
|
442
|
+
'zitejs/integrations': ['/app/.zite/integrations/index.ts'],
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
const host = typescript_1.default.createCompilerHost(options);
|
|
446
|
+
const { getSourceFile, fileExists, readFile, directoryExists } = host;
|
|
447
|
+
host.fileExists = f => f in files || fileExists(f);
|
|
448
|
+
host.directoryExists = d => Object.keys(files).some(f => f.startsWith(`${d}/`)) ||
|
|
449
|
+
(directoryExists?.(d) ?? false);
|
|
450
|
+
host.readFile = f => files[f] ?? readFile(f);
|
|
451
|
+
host.getSourceFile = (f, lang, ...rest) => f in files
|
|
452
|
+
? typescript_1.default.createSourceFile(f, files[f], lang)
|
|
453
|
+
: getSourceFile(f, lang, ...rest);
|
|
454
|
+
const program = typescript_1.default.createProgram(Object.keys(files), options, host);
|
|
455
|
+
return typescript_1.default
|
|
456
|
+
.getPreEmitDiagnostics(program)
|
|
457
|
+
.filter(d => d.file?.fileName.startsWith('/app/'))
|
|
458
|
+
.map(d => typescript_1.default.flattenDiagnosticMessageText(d.messageText, ' '));
|
|
459
|
+
};
|
|
460
|
+
(0, vitest_1.it)('emits the client typed by name', () => {
|
|
461
|
+
(0, vitest_1.expect)((0, lib_js_1.generateIntegrationsIndex)([
|
|
462
|
+
{ name: 'team', serviceType: 'slack' },
|
|
463
|
+
{ name: 'crm', serviceType: 'salesforce' },
|
|
464
|
+
], { reexportAirtable: false })).toMatchInlineSnapshot(`
|
|
465
|
+
"// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.
|
|
466
|
+
// zitejs/integrations — an integration's credentials by its zite.config.json entry name.
|
|
467
|
+
//
|
|
468
|
+
// Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):
|
|
469
|
+
// import { integrations } from 'zitejs/integrations';
|
|
470
|
+
//
|
|
471
|
+
// const crm = await integrations.get('<entry name>');
|
|
472
|
+
// if (crm.status !== 'connected') { /* crm.connectUrl is where a person reconnects */ }
|
|
473
|
+
//
|
|
474
|
+
// Call get where the value is used, not once at module scope: a token refreshed
|
|
475
|
+
// mid-run is what the next call returns.
|
|
476
|
+
|
|
477
|
+
import { createIntegrationsClient } from 'zitejs/runtime';
|
|
478
|
+
import type { Connected, NeedsConnect } from 'zitejs/runtime';
|
|
479
|
+
|
|
480
|
+
export type { Connected, NeedsConnect };
|
|
481
|
+
|
|
482
|
+
export type IntegrationName = 'crm' | 'team';
|
|
483
|
+
|
|
484
|
+
export type IntegrationByName = {
|
|
485
|
+
'crm': Connected<{ instanceUrl: string }> | NeedsConnect;
|
|
486
|
+
'team': Connected | NeedsConnect;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
export const integrations = createIntegrationsClient<IntegrationByName>();
|
|
490
|
+
"
|
|
491
|
+
`);
|
|
492
|
+
});
|
|
493
|
+
(0, vitest_1.it)('types get per name in app code', () => {
|
|
494
|
+
const generated = (0, lib_js_1.generateIntegrationsIndex)([
|
|
495
|
+
{ name: 'crm', serviceType: 'salesforce' },
|
|
496
|
+
{ name: 'warehouse', serviceType: 'clickhouse' },
|
|
497
|
+
], { reexportAirtable: false });
|
|
498
|
+
(0, vitest_1.expect)(typeErrorsIn(generated, [
|
|
499
|
+
"import { integrations } from 'zitejs/integrations';",
|
|
500
|
+
'export const run = async () => {',
|
|
501
|
+
" const crm = await integrations.get('crm');",
|
|
502
|
+
" if (crm.status === 'connected') return crm.instanceUrl + crm.accessToken;",
|
|
503
|
+
" const warehouse = await integrations.get('warehouse');",
|
|
504
|
+
" if (warehouse.status === 'connected') return warehouse.url + warehouse.username;",
|
|
505
|
+
' return crm.connectUrl;',
|
|
506
|
+
'};',
|
|
507
|
+
].join('\n'))).toEqual([]);
|
|
508
|
+
(0, vitest_1.expect)(typeErrorsIn(generated, "import { integrations } from 'zitejs/integrations';\nvoid integrations.get('nope');")).toHaveLength(1);
|
|
509
|
+
});
|
|
510
|
+
(0, vitest_1.it)('compiles with nothing visible, naming nothing', () => {
|
|
511
|
+
const generated = (0, lib_js_1.generateIntegrationsIndex)([], {
|
|
512
|
+
reexportAirtable: false,
|
|
513
|
+
});
|
|
514
|
+
(0, vitest_1.expect)(generated).toContain('export type IntegrationName = never;');
|
|
515
|
+
(0, vitest_1.expect)(typeErrorsIn(generated, "import { integrations, type IntegrationName } from 'zitejs/integrations';\nexport const f = (n: IntegrationName) => n;\nexport const g = integrations;")).toEqual([]);
|
|
516
|
+
});
|
|
517
|
+
vitest_1.it.each([
|
|
518
|
+
['a double quote', 'crm"prod'],
|
|
519
|
+
['a single quote', "crm'prod"],
|
|
520
|
+
['a newline', 'crm\nprod'],
|
|
521
|
+
['a trailing backslash', 'crm\\'],
|
|
522
|
+
])('emits parseable TypeScript for a name with %s', (_what, name) => {
|
|
523
|
+
(0, vitest_1.expect)(syntaxErrorsIn((0, lib_js_1.generateIntegrationsIndex)([{ name, serviceType: 'slack' }], {
|
|
524
|
+
reexportAirtable: false,
|
|
525
|
+
}))).toEqual([]);
|
|
526
|
+
});
|
|
527
|
+
// One module for everything integration: the Airtable clients keep resolving.
|
|
528
|
+
(0, vitest_1.it)('re-exports the generated Airtable clients beside the integrations client', () => {
|
|
529
|
+
const generated = (0, lib_js_1.generateIntegrationsIndex)([{ name: 'team', serviceType: 'slack' }], { reexportAirtable: true });
|
|
530
|
+
(0, vitest_1.expect)(generated).toContain("export * from './airtable';");
|
|
531
|
+
(0, vitest_1.expect)(typeErrorsIn(generated, [
|
|
532
|
+
"import { Customers, integrations } from 'zitejs/integrations';",
|
|
533
|
+
'export const run = async () => {',
|
|
534
|
+
' await Customers.findMany();',
|
|
535
|
+
" return integrations.get('team');",
|
|
536
|
+
'};',
|
|
537
|
+
].join('\n'), 'export const Customers = { findMany: async () => [] };')).toEqual([]);
|
|
538
|
+
});
|
|
539
|
+
});
|
package/dist/esm/auth/index.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export declare const signIn: {
|
|
|
67
67
|
}, FetchOptions["throw"] extends true ? true : false>>;
|
|
68
68
|
} & {
|
|
69
69
|
social: <FetchOptions extends import("better-auth").ClientFetchOption<Partial<{
|
|
70
|
-
provider:
|
|
70
|
+
provider: "salesforce" | "slack" | "linear" | "notion" | "linkedin" | "github" | (string & {}) | "huggingface" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "gitlab" | "tiktok" | "reddit" | "roblox" | "vk" | "zoom" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
|
|
71
71
|
callbackURL?: string | undefined;
|
|
72
72
|
newUserCallbackURL?: string | undefined;
|
|
73
73
|
errorCallbackURL?: string | undefined;
|
|
@@ -91,7 +91,7 @@ export declare const signIn: {
|
|
|
91
91
|
loginHint?: string | undefined;
|
|
92
92
|
additionalData?: Record<string, any> | undefined;
|
|
93
93
|
}> & Record<string, any>, Partial<Record<string, any>> & Record<string, any>, Record<string, any> | undefined>>(data_0: import("better-auth/react").Prettify<{
|
|
94
|
-
provider:
|
|
94
|
+
provider: "salesforce" | "slack" | "linear" | "notion" | "linkedin" | "github" | (string & {}) | "huggingface" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "spotify" | "twitch" | "twitter" | "dropbox" | "kick" | "gitlab" | "tiktok" | "reddit" | "roblox" | "vk" | "zoom" | "kakao" | "naver" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
|
|
95
95
|
callbackURL?: string | undefined;
|
|
96
96
|
newUserCallbackURL?: string | undefined;
|
|
97
97
|
errorCallbackURL?: string | undefined;
|
|
@@ -22,4 +22,8 @@
|
|
|
22
22
|
import * as esbuild from 'esbuild';
|
|
23
23
|
export declare const UNSUPPORTED_IN_WORKERD: Set<string>;
|
|
24
24
|
export declare const BASE_BUILD_OPTIONS: esbuild.BuildOptions;
|
|
25
|
+
export declare function createAliasPlugin(opts: {
|
|
26
|
+
baseDir?: string;
|
|
27
|
+
sdkPath?: string;
|
|
28
|
+
}): esbuild.Plugin;
|
|
25
29
|
export declare function runBundle(): Promise<void>;
|
package/dist/esm/bundle/index.js
CHANGED
|
@@ -24,6 +24,8 @@ 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 { INTEGRATIONS_CLASS_NAME, INTEGRATIONS_SDK_INTEGRATION_ID, } from '../integrations/client.js';
|
|
28
|
+
import { SDK_NOT_INITIALIZED_MESSAGE } from '../internal/sdkCall.js';
|
|
27
29
|
import { AUTOMATION_ENDPOINT_NAME, endpointEntryFile, findSourceDir, listEndpointNames, sourceRootOf, } from '../sourceRoots.js';
|
|
28
30
|
// workerd's `nodejs_compat` does not provide these. Left external they bundle
|
|
29
31
|
// fine and then kill the worker at load time with `No such module`; excluded,
|
|
@@ -192,7 +194,7 @@ function getSdkImportSource(baseDir) {
|
|
|
192
194
|
return 'zite-integrations-backend-sdk';
|
|
193
195
|
}
|
|
194
196
|
/**
|
|
195
|
-
* `zitejs/db`, `zitejs/api
|
|
197
|
+
* `zitejs/db`, `zitejs/api` and `zitejs/email` are
|
|
196
198
|
* tsconfig aliases onto generated files, not real package subpaths — they are
|
|
197
199
|
* absent from the `exports` map. `zitejs/backend` IS a real subpath export, but
|
|
198
200
|
* it lands here too and `zitejs` is not in PREBUNDLED_LIBS, so nothing would
|
|
@@ -205,7 +207,19 @@ const unresolvedAlias = (specifier, expectedPath) => {
|
|
|
205
207
|
throw new Error(`Cannot resolve '${specifier}': expected generated file at ${expectedPath}. ` +
|
|
206
208
|
`Run \`npx zitejs generate\` before bundling.`);
|
|
207
209
|
};
|
|
208
|
-
|
|
210
|
+
const AUTH_SERVER_SHIM_PATH = 'zitejs-auth-server-shim';
|
|
211
|
+
const INTEGRATIONS_SHIM_PATH = 'zitejs-integrations-shim';
|
|
212
|
+
const VIRTUAL_MODULES = {
|
|
213
|
+
[AUTH_SERVER_SHIM_PATH]: 'export const ziteAuth = (config) => config;',
|
|
214
|
+
[INTEGRATIONS_SHIM_PATH]: `export const integrations = {
|
|
215
|
+
get(name) {
|
|
216
|
+
const sdkCall = globalThis.__wrapSdkCall;
|
|
217
|
+
if (!sdkCall) throw new Error(${JSON.stringify(SDK_NOT_INITIALIZED_MESSAGE)});
|
|
218
|
+
return sdkCall(${JSON.stringify(INTEGRATIONS_SDK_INTEGRATION_ID)}, ${JSON.stringify(INTEGRATIONS_CLASS_NAME)}, 'get', { name });
|
|
219
|
+
},
|
|
220
|
+
};`,
|
|
221
|
+
};
|
|
222
|
+
export function createAliasPlugin(opts) {
|
|
209
223
|
return {
|
|
210
224
|
name: 'zite-alias',
|
|
211
225
|
setup(build) {
|
|
@@ -256,14 +270,18 @@ function createAliasPlugin(opts) {
|
|
|
256
270
|
}
|
|
257
271
|
return unresolvedAlias('zitejs/api', `${opts.baseDir}/.zite/api.ts`);
|
|
258
272
|
});
|
|
259
|
-
//
|
|
273
|
+
// zitejs/integrations: the generated index (the integrations client plus
|
|
274
|
+
// any Airtable clients), else Airtable alone from an older generate, else
|
|
275
|
+
// this bundler's own client, whatever zitejs the project pins.
|
|
260
276
|
build.onResolve({ filter: /^zitejs\/integrations$/ }, () => {
|
|
261
277
|
if (opts.baseDir) {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
278
|
+
for (const file of ['index.ts', 'airtable.ts']) {
|
|
279
|
+
const intPath = path.resolve(opts.baseDir, '.zite/integrations', file);
|
|
280
|
+
if (fs.existsSync(intPath))
|
|
281
|
+
return { path: intPath };
|
|
282
|
+
}
|
|
265
283
|
}
|
|
266
|
-
return
|
|
284
|
+
return { path: INTEGRATIONS_SHIM_PATH, namespace: 'zite-virtual' };
|
|
267
285
|
});
|
|
268
286
|
// Resolve zitejs/email to .zite/integrations/email.ts
|
|
269
287
|
build.onResolve({ filter: /^zitejs\/email$/ }, () => {
|
|
@@ -277,11 +295,11 @@ function createAliasPlugin(opts) {
|
|
|
277
295
|
// zitejs/auth/server is types + an identity wrapper — inline a shim so
|
|
278
296
|
// zite.auth.ts bundles without resolving the installed package.
|
|
279
297
|
build.onResolve({ filter: /^zitejs\/auth\/server$/ }, () => ({
|
|
280
|
-
path:
|
|
298
|
+
path: AUTH_SERVER_SHIM_PATH,
|
|
281
299
|
namespace: 'zite-virtual',
|
|
282
300
|
}));
|
|
283
|
-
build.onLoad({ filter: /.*/, namespace: 'zite-virtual' }, () => ({
|
|
284
|
-
contents:
|
|
301
|
+
build.onLoad({ filter: /.*/, namespace: 'zite-virtual' }, (args) => ({
|
|
302
|
+
contents: VIRTUAL_MODULES[args.path],
|
|
285
303
|
loader: 'js',
|
|
286
304
|
}));
|
|
287
305
|
// zitejs/runtime is NOT in PREBUNDLED_LIBS — it's a thin fetch()
|
package/dist/esm/dev/index.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the zitejs that `fromDir` resolves, nearest first up to the project root,
|
|
3
|
+
* has the integrations client: the CLI running generate can be newer than that pin.
|
|
4
|
+
*/
|
|
5
|
+
export declare function projectRuntimeHasIntegrationsClient(fromDir: string, projectRoot?: string): boolean;
|
|
1
6
|
export declare function runGenerate(): Promise<void>;
|
|
2
7
|
export declare function runDev(): Promise<void>;
|
package/dist/esm/dev/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { watch } from "fs";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
3
|
+
import { dirname, join, resolve } from "path";
|
|
4
4
|
import { listSourceDirs } from "../sourceRoots.js";
|
|
5
5
|
import { runSync } from "../sync/index.js";
|
|
6
|
-
import { generateDbTs, generateApiTs, generateBackendWrapperTs, generateAirtableTs, findEmailIntegrationId, generateEmailSdk, } from "../sync/lib.js";
|
|
6
|
+
import { generateDbTs, generateApiTs, generateBackendWrapperTs, generateAirtableTs, findEmailIntegrationId, generateEmailSdk, generateIntegrationsIndex, } from "../sync/lib.js";
|
|
7
|
+
import { gettableIntegrations, } from "../integrations/rules.js";
|
|
7
8
|
const debounceTimers = new Map();
|
|
8
9
|
function debounce(key, fn, ms) {
|
|
9
10
|
const existing = debounceTimers.get(key);
|
|
@@ -35,6 +36,48 @@ const readJsonFile = (path) => {
|
|
|
35
36
|
function getEmailIntegrationId(appDir) {
|
|
36
37
|
return findEmailIntegrationId(readJsonFile(join(appDir.path, "zite.config.json")), readJsonFile("zite.config.json"));
|
|
37
38
|
}
|
|
39
|
+
// `{}` with no file; undefined when unreadable, so a failed read keeps the old file.
|
|
40
|
+
const readConfigIntegrations = (path) => {
|
|
41
|
+
if (!existsSync(path))
|
|
42
|
+
return {};
|
|
43
|
+
const config = readJsonFile(path);
|
|
44
|
+
if (typeof config !== "object" || config === null)
|
|
45
|
+
return undefined;
|
|
46
|
+
const { integrations } = config;
|
|
47
|
+
if (integrations == null)
|
|
48
|
+
return {};
|
|
49
|
+
return typeof integrations === "object" && !Array.isArray(integrations)
|
|
50
|
+
? integrations
|
|
51
|
+
: undefined;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Whether the zitejs that `fromDir` resolves, nearest first up to the project root,
|
|
55
|
+
* has the integrations client: the CLI running generate can be newer than that pin.
|
|
56
|
+
*/
|
|
57
|
+
export function projectRuntimeHasIntegrationsClient(fromDir, projectRoot = process.cwd()) {
|
|
58
|
+
const root = resolve(projectRoot);
|
|
59
|
+
for (let dir = resolve(fromDir);; dir = dirname(dir)) {
|
|
60
|
+
const pkg = readJsonFile(join(dir, "node_modules", "zitejs", "package.json"));
|
|
61
|
+
if (pkg)
|
|
62
|
+
return pkg.exports?.["./integrations"] !== undefined;
|
|
63
|
+
if (dir === root || dirname(dir) === dir)
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Runs after the Airtable step, so the index re-exports its clients when they exist.
|
|
68
|
+
function regenerateAppIntegrationsIndex(appDir) {
|
|
69
|
+
if (!projectRuntimeHasIntegrationsClient(appDir.path))
|
|
70
|
+
return;
|
|
71
|
+
const root = readConfigIntegrations("zite.config.json");
|
|
72
|
+
const own = readConfigIntegrations(join(appDir.path, "zite.config.json"));
|
|
73
|
+
if (!root || !own)
|
|
74
|
+
return;
|
|
75
|
+
const intDir = join(appDir.path, ".zite", "integrations");
|
|
76
|
+
mkdirSync(intDir, { recursive: true });
|
|
77
|
+
writeFileSync(join(intDir, "index.ts"), generateIntegrationsIndex(gettableIntegrations(root, own), {
|
|
78
|
+
reexportAirtable: existsSync(join(intDir, "airtable.ts")),
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
38
81
|
function getDeclaredEnvVarNames(appDir) {
|
|
39
82
|
try {
|
|
40
83
|
const configPath = join(appDir.path, "zite.config.json");
|
|
@@ -149,6 +192,7 @@ export async function runGenerate() {
|
|
|
149
192
|
regenerateAppApiTs(app);
|
|
150
193
|
regenerateAppTypedWrappers(app);
|
|
151
194
|
regenerateAppAirtableSdk(app);
|
|
195
|
+
regenerateAppIntegrationsIndex(app);
|
|
152
196
|
}
|
|
153
197
|
console.log("Done!");
|
|
154
198
|
}
|
|
@@ -169,6 +213,7 @@ export async function runDev() {
|
|
|
169
213
|
for (const app of appDirs) {
|
|
170
214
|
regenerateAppApiTs(app);
|
|
171
215
|
regenerateAppTypedWrappers(app);
|
|
216
|
+
regenerateAppIntegrationsIndex(app);
|
|
172
217
|
}
|
|
173
218
|
// 3. Watch each dir's src/api/ for endpoint changes
|
|
174
219
|
let watchingAny = false;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { Zite } from "./client/index.js";
|
|
2
2
|
export type { Record, RecordListOutput, RecordListOptions, FieldCreateInput, FieldUpdateInput, TableCreateInput, TableUpdateInput, DatabaseCreateInput, DatabaseListOutput, } from "./client/index.js";
|
|
3
3
|
export * from "./types/index.js";
|
|
4
|
+
export { CREDENTIAL_FIELDS, NON_CREDENTIAL_SERVICE_TYPES, gettableIntegrations, isCredentialServiceType, normalizeIntegrationServiceType, } from "./integrations/rules.js";
|
|
5
|
+
export type { ConfigIntegrations, CredentialServiceType, GettableIntegration, } from "./integrations/rules.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const INTEGRATIONS_SDK_INTEGRATION_ID = "__connections__";
|
|
2
|
+
export declare const INTEGRATIONS_CLASS_NAME = "Connections";
|
|
3
|
+
/**
|
|
4
|
+
* `accessToken` is the service's primary credential (for Twilio the account SID).
|
|
5
|
+
* Call `get` where the value is used: a token refreshed mid-run is what the next call returns.
|
|
6
|
+
*/
|
|
7
|
+
export type Connected<Extra = {}> = {
|
|
8
|
+
status: "connected";
|
|
9
|
+
accessToken: string;
|
|
10
|
+
} & Extra;
|
|
11
|
+
/** No account behind the entry, or its token could not be refreshed. `connectUrl` is where a person fixes that. */
|
|
12
|
+
export type NeedsConnect = {
|
|
13
|
+
status: "missing" | "expired";
|
|
14
|
+
connectUrl: string;
|
|
15
|
+
};
|
|
16
|
+
export interface IntegrationsClient<ByName extends Record<string, unknown>> {
|
|
17
|
+
/** The credentials of the `zite.config.json` integration entry named `name`. */
|
|
18
|
+
get<Name extends keyof ByName & string>(name: Name): Promise<ByName[Name]>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* `ByName` maps each entry name to its answer, e.g.
|
|
22
|
+
* `{ crm: Connected<{ instanceUrl: string }> | NeedsConnect }`; `zitejs generate`
|
|
23
|
+
* writes it per app into `.zite/integrations/index.ts`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createIntegrationsClient<ByName extends Record<string, unknown>>(): IntegrationsClient<ByName>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getSdkCall } from "../internal/sdkCall.js";
|
|
2
|
+
// The worker and runner answer this call under its original name; renaming the
|
|
3
|
+
// wire protocol would break every deployed bundle for no user-visible gain.
|
|
4
|
+
export const INTEGRATIONS_SDK_INTEGRATION_ID = "__connections__";
|
|
5
|
+
export const INTEGRATIONS_CLASS_NAME = "Connections";
|
|
6
|
+
/**
|
|
7
|
+
* `ByName` maps each entry name to its answer, e.g.
|
|
8
|
+
* `{ crm: Connected<{ instanceUrl: string }> | NeedsConnect }`; `zitejs generate`
|
|
9
|
+
* writes it per app into `.zite/integrations/index.ts`.
|
|
10
|
+
*/
|
|
11
|
+
export function createIntegrationsClient() {
|
|
12
|
+
return {
|
|
13
|
+
get(name) {
|
|
14
|
+
return getSdkCall()(INTEGRATIONS_SDK_INTEGRATION_ID, INTEGRATIONS_CLASS_NAME, "get", { name });
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expectTypeOf } from "vitest";
|
|
2
|
+
import { createIntegrationsClient, } from "./client.js";
|
|
3
|
+
import { integrations as untyped } from "./index.js";
|
|
4
|
+
import { CREDENTIAL_FIELDS, isCredentialServiceType } from "../index.js";
|
|
5
|
+
describe("createIntegrationsClient", () => {
|
|
6
|
+
const client = createIntegrationsClient();
|
|
7
|
+
it("types each name's answer", () => {
|
|
8
|
+
expectTypeOf(client.get("crm")).resolves.toEqualTypeOf();
|
|
9
|
+
});
|
|
10
|
+
it("rejects a name the app cannot see", () => {
|
|
11
|
+
// @ts-expect-error not a visible entry
|
|
12
|
+
void client.get("nope");
|
|
13
|
+
});
|
|
14
|
+
// What `.zite/integrations/index.ts` emits when nothing is visible.
|
|
15
|
+
it("accepts an empty map, naming nothing", () => {
|
|
16
|
+
const none = createIntegrationsClient();
|
|
17
|
+
expectTypeOf().toEqualTypeOf();
|
|
18
|
+
});
|
|
19
|
+
it("narrows to the connected fields on status", async () => {
|
|
20
|
+
const crm = await client.get("crm");
|
|
21
|
+
if (crm.status === "connected") {
|
|
22
|
+
expectTypeOf(crm.instanceUrl).toEqualTypeOf();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
expectTypeOf(crm.connectUrl).toEqualTypeOf();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("the untyped zitejs/integrations client", () => {
|
|
30
|
+
it("takes any name; only accessToken is certain", async () => {
|
|
31
|
+
const entry = await untyped.get("anything");
|
|
32
|
+
if (entry.status === "connected") {
|
|
33
|
+
expectTypeOf(entry.accessToken).toEqualTypeOf();
|
|
34
|
+
expectTypeOf(entry.instanceUrl).toEqualTypeOf();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe("CREDENTIAL_FIELDS", () => {
|
|
39
|
+
it("is keyed by service type, and a string narrows to one", () => {
|
|
40
|
+
expectTypeOf(CREDENTIAL_FIELDS.salesforce).toEqualTypeOf();
|
|
41
|
+
const type = "salesforce";
|
|
42
|
+
// @ts-expect-error a plain string is not a service type
|
|
43
|
+
void CREDENTIAL_FIELDS[type];
|
|
44
|
+
if (isCredentialServiceType(type)) {
|
|
45
|
+
expectTypeOf(CREDENTIAL_FIELDS[type]).toEqualTypeOf();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type Connected, type NeedsConnect } from "./client.js";
|
|
2
|
+
export type { Connected, NeedsConnect } from "./client.js";
|
|
3
|
+
export declare const integrations: import("./client.js").IntegrationsClient<Record<string, NeedsConnect | Connected<Partial<Record<string, string>>>>>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
declare const FIELDS_BY_TYPE: {
|
|
2
|
+
googleCalendar: readonly string[];
|
|
3
|
+
googleDrive: readonly string[];
|
|
4
|
+
gmail: readonly string[];
|
|
5
|
+
googleDocs: readonly string[];
|
|
6
|
+
hubspot: readonly string[];
|
|
7
|
+
salesforce: readonly string[];
|
|
8
|
+
twilio: readonly string[];
|
|
9
|
+
slack: readonly string[];
|
|
10
|
+
linear: readonly string[];
|
|
11
|
+
openai: readonly string[];
|
|
12
|
+
googlesheets: readonly string[];
|
|
13
|
+
microsoftTeams: readonly string[];
|
|
14
|
+
onedrive: readonly string[];
|
|
15
|
+
outlook: readonly string[];
|
|
16
|
+
intercom: readonly string[];
|
|
17
|
+
mailchimp: readonly string[];
|
|
18
|
+
anthropic: readonly string[];
|
|
19
|
+
gemini: readonly string[];
|
|
20
|
+
stripe: readonly string[];
|
|
21
|
+
trello: readonly string[];
|
|
22
|
+
notion: readonly string[];
|
|
23
|
+
facebookPages: readonly string[];
|
|
24
|
+
monday: readonly string[];
|
|
25
|
+
resend: readonly string[];
|
|
26
|
+
clickhouse: readonly string[];
|
|
27
|
+
amplitude: readonly string[];
|
|
28
|
+
elevenlabs: readonly string[];
|
|
29
|
+
brevo: readonly string[];
|
|
30
|
+
calendly: readonly string[];
|
|
31
|
+
asana: readonly string[];
|
|
32
|
+
xero: readonly string[];
|
|
33
|
+
googleForms: readonly string[];
|
|
34
|
+
linkedin: readonly string[];
|
|
35
|
+
github: readonly string[];
|
|
36
|
+
quickbooks: readonly string[];
|
|
37
|
+
firecrawl: readonly string[];
|
|
38
|
+
jira: readonly string[];
|
|
39
|
+
typeform: readonly string[];
|
|
40
|
+
apollo: readonly string[];
|
|
41
|
+
attio: readonly string[];
|
|
42
|
+
};
|
|
43
|
+
/** A service type `integrations.get` serves: one used through its npm package with a token. */
|
|
44
|
+
export type CredentialServiceType = keyof typeof FIELDS_BY_TYPE;
|
|
45
|
+
/** Per service type: `accessToken`, then its extra labels sorted (Salesforce `instanceUrl`). */
|
|
46
|
+
export declare const CREDENTIAL_FIELDS: Readonly<Record<CredentialServiceType, readonly string[]>>;
|
|
47
|
+
/**
|
|
48
|
+
* Types the runtime never serves: those with a generated client (Airtable, email,
|
|
49
|
+
* the project database, Fillout) and those whose key is a frontend value (Maps, Analytics).
|
|
50
|
+
*/
|
|
51
|
+
export declare const NON_CREDENTIAL_SERVICE_TYPES: readonly string[];
|
|
52
|
+
export declare const normalizeIntegrationServiceType: (type: string) => string;
|
|
53
|
+
export declare const isCredentialServiceType: (type: string) => type is CredentialServiceType;
|
|
54
|
+
/** One entry `integrations.get` can name from a source dir. */
|
|
55
|
+
export type GettableIntegration = {
|
|
56
|
+
name: string;
|
|
57
|
+
serviceType: CredentialServiceType;
|
|
58
|
+
};
|
|
59
|
+
export type ConfigIntegrations = Readonly<Record<string, {
|
|
60
|
+
type?: unknown;
|
|
61
|
+
} | undefined>>;
|
|
62
|
+
/**
|
|
63
|
+
* The entries a source dir can name, by name: its own, then the workspace's under
|
|
64
|
+
* keys it has not taken. An entry whose type the runtime would not serve is dropped.
|
|
65
|
+
*/
|
|
66
|
+
export declare const gettableIntegrations: (rootIntegrations: ConfigIntegrations | undefined, appIntegrations?: ConfigIntegrations) => GettableIntegration[];
|
|
67
|
+
export {};
|