zitejs 0.9.122 → 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 +5 -1
- package/dist/cjs/bundle/index.js +46 -26
- package/dist/cjs/check/index.js +2 -2
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/dev/index.d.ts +5 -0
- package/dist/cjs/dev/index.js +47 -1
- 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/sourceRoots.d.ts +13 -0
- package/dist/cjs/sourceRoots.js +38 -1
- package/dist/cjs/sourceRoots.test.d.ts +1 -0
- package/dist/cjs/sourceRoots.test.js +30 -0
- 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 +5 -1
- package/dist/esm/bundle/index.js +47 -28
- package/dist/esm/check/index.js +3 -3
- package/dist/esm/cli.js +1 -1
- package/dist/esm/dev/index.d.ts +5 -0
- package/dist/esm/dev/index.js +48 -3
- 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/sourceRoots.d.ts +13 -0
- package/dist/esm/sourceRoots.js +35 -1
- package/dist/esm/sourceRoots.test.d.ts +1 -0
- package/dist/esm/sourceRoots.test.js +28 -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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { CREDENTIAL_FIELDS, NON_CREDENTIAL_SERVICE_TYPES, isCredentialServiceType, normalizeIntegrationServiceType, gettableIntegrations, } from "../index.js";
|
|
3
|
+
describe("CREDENTIAL_FIELDS", () => {
|
|
4
|
+
it("answers accessToken, then the service's extras sorted", () => {
|
|
5
|
+
expect(CREDENTIAL_FIELDS.slack).toEqual(["accessToken"]);
|
|
6
|
+
expect(CREDENTIAL_FIELDS.salesforce).toEqual(["accessToken", "instanceUrl"]);
|
|
7
|
+
expect(CREDENTIAL_FIELDS.clickhouse).toEqual([
|
|
8
|
+
"accessToken",
|
|
9
|
+
"url",
|
|
10
|
+
"username",
|
|
11
|
+
]);
|
|
12
|
+
expect(CREDENTIAL_FIELDS.quickbooks).toEqual([
|
|
13
|
+
"accessToken",
|
|
14
|
+
"apiBaseUrl",
|
|
15
|
+
"realmId",
|
|
16
|
+
]);
|
|
17
|
+
expect(CREDENTIAL_FIELDS.jira).toEqual(["accessToken", "cloudId", "siteUrl"]);
|
|
18
|
+
});
|
|
19
|
+
it("has no type the runtime never serves", () => {
|
|
20
|
+
for (const type of NON_CREDENTIAL_SERVICE_TYPES) {
|
|
21
|
+
expect(isCredentialServiceType(type)).toBe(false);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
it("does not treat Object.prototype names as service types", () => {
|
|
25
|
+
expect(isCredentialServiceType("toString")).toBe(false);
|
|
26
|
+
expect(normalizeIntegrationServiceType("constructor")).toBe("constructor");
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("normalizeIntegrationServiceType", () => {
|
|
30
|
+
it.each([
|
|
31
|
+
["googlecalendar", "googleCalendar"],
|
|
32
|
+
["google-drive", "googleDrive"],
|
|
33
|
+
["google-sheets", "googlesheets"],
|
|
34
|
+
["microsoft-teams", "microsoftTeams"],
|
|
35
|
+
["one-drive", "onedrive"],
|
|
36
|
+
])("maps %s to %s", (legacy, canonical) => {
|
|
37
|
+
expect(normalizeIntegrationServiceType(legacy)).toBe(canonical);
|
|
38
|
+
expect(isCredentialServiceType(canonical)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it("leaves a canonical type alone", () => {
|
|
41
|
+
expect(normalizeIntegrationServiceType("slack")).toBe("slack");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("gettableIntegrations", () => {
|
|
45
|
+
it("lists the app's entries, then the workspace's under untaken keys, by name", () => {
|
|
46
|
+
expect(gettableIntegrations({ crm: { type: "salesforce" }, team: { type: "slack" } }, { billing: { type: "stripe" }, team: { type: "microsoftTeams" } })).toEqual([
|
|
47
|
+
{ name: "billing", serviceType: "stripe" },
|
|
48
|
+
{ name: "crm", serviceType: "salesforce" },
|
|
49
|
+
{ name: "team", serviceType: "microsoftTeams" },
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
it("lets an app entry of a generated-client type shadow a workspace credential entry", () => {
|
|
53
|
+
expect(gettableIntegrations({ mail: { type: "gmail" } }, { mail: { type: "email" } })).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
it("drops types the runtime never serves and entries without a type", () => {
|
|
56
|
+
expect(gettableIntegrations({
|
|
57
|
+
base: { type: "airtable" },
|
|
58
|
+
mail: { type: "email" },
|
|
59
|
+
db: { type: "databases" },
|
|
60
|
+
form: { type: "fillout" },
|
|
61
|
+
map: { type: "google-maps" },
|
|
62
|
+
ga: { type: "googleAnalytics" },
|
|
63
|
+
broken: {},
|
|
64
|
+
numeric: { type: 7 },
|
|
65
|
+
gone: undefined,
|
|
66
|
+
})).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
it("normalizes legacy type spellings", () => {
|
|
69
|
+
expect(gettableIntegrations({ cal: { type: "google-calendar" } })).toEqual([
|
|
70
|
+
{ name: "cal", serviceType: "googleCalendar" },
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
it("drops a type it does not know, as the runtime does", () => {
|
|
74
|
+
expect(gettableIntegrations({ next: { type: "someNewService" } })).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
it("shadows by own keys only", () => {
|
|
77
|
+
expect(gettableIntegrations({ constructor: { type: "slack" } }, {})).toEqual([
|
|
78
|
+
{ name: "constructor", serviceType: "slack" },
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
it("works with no configs", () => {
|
|
82
|
+
expect(gettableIntegrations(undefined)).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export type SdkCall = (integrationId: string, className: string, methodName: string, params?: unknown) => Promise<unknown>;
|
|
2
|
+
export declare const SDK_NOT_INITIALIZED_MESSAGE = "Zite SDK runtime not initialized. Endpoints must run inside the Zite worker.";
|
|
2
3
|
export declare function getSdkCall(): SdkCall;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
export const SDK_NOT_INITIALIZED_MESSAGE = 'Zite SDK runtime not initialized. Endpoints must run inside the Zite worker.';
|
|
1
2
|
export function getSdkCall() {
|
|
2
3
|
const fn = globalThis.__wrapSdkCall;
|
|
3
4
|
if (!fn) {
|
|
4
|
-
throw new Error(
|
|
5
|
+
throw new Error(SDK_NOT_INITIALIZED_MESSAGE);
|
|
5
6
|
}
|
|
6
7
|
return fn;
|
|
7
8
|
}
|
|
@@ -332,5 +332,6 @@ export interface EmailClient {
|
|
|
332
332
|
* app's connected email integration key.
|
|
333
333
|
*/
|
|
334
334
|
export declare function createEmailClient(integrationId: string): EmailClient;
|
|
335
|
+
export { createIntegrationsClient, type Connected, type IntegrationsClient, type NeedsConnect, } from "../integrations/client.js";
|
|
335
336
|
export { createCaller } from "../caller/index.js";
|
|
336
337
|
export type { EndpointConfig } from "../caller/index.js";
|
|
@@ -203,4 +203,5 @@ export function createEmailClient(integrationId) {
|
|
|
203
203
|
send: (params) => getSdkCall()(integrationId, "Email", "send", params),
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
|
+
export { createIntegrationsClient, } from "../integrations/client.js";
|
|
206
207
|
export { createCaller } from "../caller/index.js";
|
|
@@ -17,3 +17,16 @@ export interface SourceDir {
|
|
|
17
17
|
export declare function listSourceDirs(cwd?: string): SourceDir[];
|
|
18
18
|
/** The source dir with this bare name, under whichever root holds it. */
|
|
19
19
|
export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* An automation is one workflow: its single endpoint is `src/workflow.ts` and
|
|
22
|
+
* its name is fixed, so nothing about an automation is keyed on a file name
|
|
23
|
+
* the way an app's `src/api/<name>.ts` endpoints are.
|
|
24
|
+
*/
|
|
25
|
+
export declare const AUTOMATION_ENDPOINT_NAME = "workflow";
|
|
26
|
+
export declare const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
|
|
27
|
+
/** The root a source dir sits under, read from its parent directory. */
|
|
28
|
+
export declare function sourceRootOf(sourceDirPath: string): SourceRoot | undefined;
|
|
29
|
+
/** An endpoint's entry file, relative to the source dir. */
|
|
30
|
+
export declare function endpointEntryFile(root: SourceRoot, name: string): string;
|
|
31
|
+
/** Every endpoint name a source dir has on disk. */
|
|
32
|
+
export declare function listEndpointNames(root: SourceRoot, sourceDirPath: string): string[];
|
package/dist/esm/sourceRoots.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readdirSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
2
|
+
import { basename, dirname, join, resolve } from "path";
|
|
3
3
|
/**
|
|
4
4
|
* The repo-root directories a project's own source lives under: `apps/` for
|
|
5
5
|
* apps and `automations/` for automations (endpoints with no frontend). The
|
|
@@ -26,3 +26,37 @@ export function listSourceDirs(cwd = ".") {
|
|
|
26
26
|
export function findSourceDir(name, cwd = ".") {
|
|
27
27
|
return listSourceDirs(cwd).find((d) => d.dir === name);
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* An automation is one workflow: its single endpoint is `src/workflow.ts` and
|
|
31
|
+
* its name is fixed, so nothing about an automation is keyed on a file name
|
|
32
|
+
* the way an app's `src/api/<name>.ts` endpoints are.
|
|
33
|
+
*/
|
|
34
|
+
export const AUTOMATION_ENDPOINT_NAME = "workflow";
|
|
35
|
+
export const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
|
|
36
|
+
/** The root a source dir sits under, read from its parent directory. */
|
|
37
|
+
export function sourceRootOf(sourceDirPath) {
|
|
38
|
+
const parent = basename(dirname(resolve(sourceDirPath)));
|
|
39
|
+
return SOURCE_ROOTS.includes(parent)
|
|
40
|
+
? parent
|
|
41
|
+
: undefined;
|
|
42
|
+
}
|
|
43
|
+
/** An endpoint's entry file, relative to the source dir. */
|
|
44
|
+
export function endpointEntryFile(root, name) {
|
|
45
|
+
return root === "automations"
|
|
46
|
+
? AUTOMATION_WORKFLOW_FILE
|
|
47
|
+
: join("src", "api", `${name}.ts`);
|
|
48
|
+
}
|
|
49
|
+
/** Every endpoint name a source dir has on disk. */
|
|
50
|
+
export function listEndpointNames(root, sourceDirPath) {
|
|
51
|
+
if (root === "automations") {
|
|
52
|
+
return existsSync(join(sourceDirPath, AUTOMATION_WORKFLOW_FILE))
|
|
53
|
+
? [AUTOMATION_ENDPOINT_NAME]
|
|
54
|
+
: [];
|
|
55
|
+
}
|
|
56
|
+
const apiDir = join(sourceDirPath, "src", "api");
|
|
57
|
+
return existsSync(apiDir)
|
|
58
|
+
? readdirSync(apiDir)
|
|
59
|
+
.filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts"))
|
|
60
|
+
.map((f) => f.replace(/\.ts$/, ""))
|
|
61
|
+
: [];
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from "fs";
|
|
2
|
+
import { tmpdir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { endpointEntryFile, listEndpointNames, sourceRootOf, } from "./sourceRoots.js";
|
|
6
|
+
describe("source roots", () => {
|
|
7
|
+
it("reads the root from the source dir's parent", () => {
|
|
8
|
+
expect(sourceRootOf("/repo/apps/crm")).toBe("apps");
|
|
9
|
+
expect(sourceRootOf("/repo/automations/digest")).toBe("automations");
|
|
10
|
+
expect(sourceRootOf("/repo/packages/ui")).toBeUndefined();
|
|
11
|
+
});
|
|
12
|
+
it("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/workflow.ts", () => {
|
|
13
|
+
expect(endpointEntryFile("apps", "listItems")).toBe("src/api/listItems.ts");
|
|
14
|
+
expect(endpointEntryFile("automations", "workflow")).toBe("src/workflow.ts");
|
|
15
|
+
});
|
|
16
|
+
it("lists what is on disk under each layout", () => {
|
|
17
|
+
const repo = mkdtempSync(join(tmpdir(), "zitejs-roots-"));
|
|
18
|
+
mkdirSync(join(repo, "apps/crm/src/api"), { recursive: true });
|
|
19
|
+
writeFileSync(join(repo, "apps/crm/src/api/listItems.ts"), "");
|
|
20
|
+
writeFileSync(join(repo, "apps/crm/src/api/apiTypes.d.ts"), "");
|
|
21
|
+
mkdirSync(join(repo, "automations/digest/src"), { recursive: true });
|
|
22
|
+
mkdirSync(join(repo, "automations/empty/src"), { recursive: true });
|
|
23
|
+
writeFileSync(join(repo, "automations/digest/src/workflow.ts"), "");
|
|
24
|
+
expect(listEndpointNames("apps", join(repo, "apps/crm"))).toEqual(["listItems"]);
|
|
25
|
+
expect(listEndpointNames("automations", join(repo, "automations/digest"))).toEqual(["workflow"]);
|
|
26
|
+
expect(listEndpointNames("automations", join(repo, "automations/empty"))).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
});
|
package/dist/esm/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/esm/sync/lib.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parse } from "@babel/parser";
|
|
2
2
|
import { allocateSchemaNames, describeRenames, keepValidSdkName, normalizeSchemaNames, SdkNameAllocator, toCamelCase, toPascalCase, toSdkName, } from "./sdkNames.js";
|
|
3
|
+
import { CREDENTIAL_FIELDS, } from "../integrations/rules.js";
|
|
3
4
|
const AUTH_USERS_TABLE_ID = "zite_user";
|
|
4
5
|
/**
|
|
5
6
|
* What a field's value looks like when a record is READ back.
|
|
@@ -1383,3 +1384,54 @@ export function generateEmailSdk(integrationId) {
|
|
|
1383
1384
|
"",
|
|
1384
1385
|
].join("\n");
|
|
1385
1386
|
}
|
|
1387
|
+
const connectedTypeFor = (serviceType) => {
|
|
1388
|
+
const extraLabels = CREDENTIAL_FIELDS[serviceType].slice(1);
|
|
1389
|
+
return extraLabels.length === 0
|
|
1390
|
+
? "Connected"
|
|
1391
|
+
: `Connected<{ ${extraLabels.map((label) => `${label}: string`).join("; ")} }>`;
|
|
1392
|
+
};
|
|
1393
|
+
/**
|
|
1394
|
+
* `.zite/integrations/index.ts`: `zitejs/integrations` for one source dir. The
|
|
1395
|
+
* `integrations` client typed by what the dir can see, plus the generated
|
|
1396
|
+
* Airtable clients when it has them.
|
|
1397
|
+
*/
|
|
1398
|
+
export function generateIntegrationsIndex(entries, opts) {
|
|
1399
|
+
const sorted = [...entries].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1400
|
+
const nameUnion = sorted.length === 0
|
|
1401
|
+
? "never"
|
|
1402
|
+
: sorted.map(({ name }) => tsSingleQuoted(name)).join(" | ");
|
|
1403
|
+
const byName = sorted.length === 0
|
|
1404
|
+
? ["export type IntegrationByName = {};"]
|
|
1405
|
+
: [
|
|
1406
|
+
"export type IntegrationByName = {",
|
|
1407
|
+
...sorted.map(({ name, serviceType }) => ` ${tsSingleQuoted(name)}: ${connectedTypeFor(serviceType)} | NeedsConnect;`),
|
|
1408
|
+
"};",
|
|
1409
|
+
];
|
|
1410
|
+
return [
|
|
1411
|
+
"// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.",
|
|
1412
|
+
"// zitejs/integrations — an integration's credentials by its zite.config.json entry name.",
|
|
1413
|
+
"//",
|
|
1414
|
+
"// Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):",
|
|
1415
|
+
"// import { integrations } from 'zitejs/integrations';",
|
|
1416
|
+
"//",
|
|
1417
|
+
"// const crm = await integrations.get('<entry name>');",
|
|
1418
|
+
"// if (crm.status !== 'connected') { /* crm.connectUrl is where a person reconnects */ }",
|
|
1419
|
+
"//",
|
|
1420
|
+
"// Call get where the value is used, not once at module scope: a token refreshed",
|
|
1421
|
+
"// mid-run is what the next call returns.",
|
|
1422
|
+
"",
|
|
1423
|
+
// Not 'zitejs/integrations': apps alias that specifier to this very file.
|
|
1424
|
+
"import { createIntegrationsClient } from 'zitejs/runtime';",
|
|
1425
|
+
"import type { Connected, NeedsConnect } from 'zitejs/runtime';",
|
|
1426
|
+
"",
|
|
1427
|
+
...(opts.reexportAirtable ? ["export * from './airtable';", ""] : []),
|
|
1428
|
+
"export type { Connected, NeedsConnect };",
|
|
1429
|
+
"",
|
|
1430
|
+
`export type IntegrationName = ${nameUnion};`,
|
|
1431
|
+
"",
|
|
1432
|
+
...byName,
|
|
1433
|
+
"",
|
|
1434
|
+
"export const integrations = createIntegrationsClient<IntegrationByName>();",
|
|
1435
|
+
"",
|
|
1436
|
+
].join("\n");
|
|
1437
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import ts from 'typescript';
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
import { findEmailIntegrationId, generateAirtableTs, generateApiTs, generateBackendWrapperTs, generateDbTs, generateIntegrationsIndex, generateEmailSdk, } from './lib.js';
|
|
4
5
|
/**
|
|
5
6
|
* Syntax errors in the emitted source. `.zite/db.ts` sits at the repo root and
|
|
6
7
|
* every app imports it, so anything unparseable here fails typecheck for the
|
|
@@ -413,3 +414,121 @@ describe('findEmailIntegrationId', () => {
|
|
|
413
414
|
expect(findEmailIntegrationId(undefined, undefined)).toBeUndefined();
|
|
414
415
|
});
|
|
415
416
|
});
|
|
417
|
+
describe('generateIntegrationsIndex', () => {
|
|
418
|
+
// Not import.meta: this file also compiles to CommonJS. Vitest runs from the repo root.
|
|
419
|
+
const runtimePath = resolve('src/runtime/index.ts');
|
|
420
|
+
/** Type errors in `usage` compiled beside the generated file, `zitejs/runtime` mapped to this source. */
|
|
421
|
+
const typeErrorsIn = (generated, usage, airtable) => {
|
|
422
|
+
const files = {
|
|
423
|
+
'/app/.zite/integrations/index.ts': generated,
|
|
424
|
+
...(airtable ? { '/app/.zite/integrations/airtable.ts': airtable } : {}),
|
|
425
|
+
'/app/src/usage.ts': usage,
|
|
426
|
+
};
|
|
427
|
+
const options = {
|
|
428
|
+
strict: true,
|
|
429
|
+
noEmit: true,
|
|
430
|
+
target: ts.ScriptTarget.ES2022,
|
|
431
|
+
module: ts.ModuleKind.ESNext,
|
|
432
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
433
|
+
skipLibCheck: true,
|
|
434
|
+
baseUrl: '/app',
|
|
435
|
+
paths: {
|
|
436
|
+
'zitejs/runtime': [runtimePath],
|
|
437
|
+
'zitejs/integrations': ['/app/.zite/integrations/index.ts'],
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
const host = ts.createCompilerHost(options);
|
|
441
|
+
const { getSourceFile, fileExists, readFile, directoryExists } = host;
|
|
442
|
+
host.fileExists = f => f in files || fileExists(f);
|
|
443
|
+
host.directoryExists = d => Object.keys(files).some(f => f.startsWith(`${d}/`)) ||
|
|
444
|
+
(directoryExists?.(d) ?? false);
|
|
445
|
+
host.readFile = f => files[f] ?? readFile(f);
|
|
446
|
+
host.getSourceFile = (f, lang, ...rest) => f in files
|
|
447
|
+
? ts.createSourceFile(f, files[f], lang)
|
|
448
|
+
: getSourceFile(f, lang, ...rest);
|
|
449
|
+
const program = ts.createProgram(Object.keys(files), options, host);
|
|
450
|
+
return ts
|
|
451
|
+
.getPreEmitDiagnostics(program)
|
|
452
|
+
.filter(d => d.file?.fileName.startsWith('/app/'))
|
|
453
|
+
.map(d => ts.flattenDiagnosticMessageText(d.messageText, ' '));
|
|
454
|
+
};
|
|
455
|
+
it('emits the client typed by name', () => {
|
|
456
|
+
expect(generateIntegrationsIndex([
|
|
457
|
+
{ name: 'team', serviceType: 'slack' },
|
|
458
|
+
{ name: 'crm', serviceType: 'salesforce' },
|
|
459
|
+
], { reexportAirtable: false })).toMatchInlineSnapshot(`
|
|
460
|
+
"// Auto-generated by zitejs generate from zite.config.json. Do not edit manually.
|
|
461
|
+
// zitejs/integrations — an integration's credentials by its zite.config.json entry name.
|
|
462
|
+
//
|
|
463
|
+
// Usage in endpoints (src/api/*.ts) and automations (src/workflow.ts):
|
|
464
|
+
// import { integrations } from 'zitejs/integrations';
|
|
465
|
+
//
|
|
466
|
+
// const crm = await integrations.get('<entry name>');
|
|
467
|
+
// if (crm.status !== 'connected') { /* crm.connectUrl is where a person reconnects */ }
|
|
468
|
+
//
|
|
469
|
+
// Call get where the value is used, not once at module scope: a token refreshed
|
|
470
|
+
// mid-run is what the next call returns.
|
|
471
|
+
|
|
472
|
+
import { createIntegrationsClient } from 'zitejs/runtime';
|
|
473
|
+
import type { Connected, NeedsConnect } from 'zitejs/runtime';
|
|
474
|
+
|
|
475
|
+
export type { Connected, NeedsConnect };
|
|
476
|
+
|
|
477
|
+
export type IntegrationName = 'crm' | 'team';
|
|
478
|
+
|
|
479
|
+
export type IntegrationByName = {
|
|
480
|
+
'crm': Connected<{ instanceUrl: string }> | NeedsConnect;
|
|
481
|
+
'team': Connected | NeedsConnect;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export const integrations = createIntegrationsClient<IntegrationByName>();
|
|
485
|
+
"
|
|
486
|
+
`);
|
|
487
|
+
});
|
|
488
|
+
it('types get per name in app code', () => {
|
|
489
|
+
const generated = generateIntegrationsIndex([
|
|
490
|
+
{ name: 'crm', serviceType: 'salesforce' },
|
|
491
|
+
{ name: 'warehouse', serviceType: 'clickhouse' },
|
|
492
|
+
], { reexportAirtable: false });
|
|
493
|
+
expect(typeErrorsIn(generated, [
|
|
494
|
+
"import { integrations } from 'zitejs/integrations';",
|
|
495
|
+
'export const run = async () => {',
|
|
496
|
+
" const crm = await integrations.get('crm');",
|
|
497
|
+
" if (crm.status === 'connected') return crm.instanceUrl + crm.accessToken;",
|
|
498
|
+
" const warehouse = await integrations.get('warehouse');",
|
|
499
|
+
" if (warehouse.status === 'connected') return warehouse.url + warehouse.username;",
|
|
500
|
+
' return crm.connectUrl;',
|
|
501
|
+
'};',
|
|
502
|
+
].join('\n'))).toEqual([]);
|
|
503
|
+
expect(typeErrorsIn(generated, "import { integrations } from 'zitejs/integrations';\nvoid integrations.get('nope');")).toHaveLength(1);
|
|
504
|
+
});
|
|
505
|
+
it('compiles with nothing visible, naming nothing', () => {
|
|
506
|
+
const generated = generateIntegrationsIndex([], {
|
|
507
|
+
reexportAirtable: false,
|
|
508
|
+
});
|
|
509
|
+
expect(generated).toContain('export type IntegrationName = never;');
|
|
510
|
+
expect(typeErrorsIn(generated, "import { integrations, type IntegrationName } from 'zitejs/integrations';\nexport const f = (n: IntegrationName) => n;\nexport const g = integrations;")).toEqual([]);
|
|
511
|
+
});
|
|
512
|
+
it.each([
|
|
513
|
+
['a double quote', 'crm"prod'],
|
|
514
|
+
['a single quote', "crm'prod"],
|
|
515
|
+
['a newline', 'crm\nprod'],
|
|
516
|
+
['a trailing backslash', 'crm\\'],
|
|
517
|
+
])('emits parseable TypeScript for a name with %s', (_what, name) => {
|
|
518
|
+
expect(syntaxErrorsIn(generateIntegrationsIndex([{ name, serviceType: 'slack' }], {
|
|
519
|
+
reexportAirtable: false,
|
|
520
|
+
}))).toEqual([]);
|
|
521
|
+
});
|
|
522
|
+
// One module for everything integration: the Airtable clients keep resolving.
|
|
523
|
+
it('re-exports the generated Airtable clients beside the integrations client', () => {
|
|
524
|
+
const generated = generateIntegrationsIndex([{ name: 'team', serviceType: 'slack' }], { reexportAirtable: true });
|
|
525
|
+
expect(generated).toContain("export * from './airtable';");
|
|
526
|
+
expect(typeErrorsIn(generated, [
|
|
527
|
+
"import { Customers, integrations } from 'zitejs/integrations';",
|
|
528
|
+
'export const run = async () => {',
|
|
529
|
+
' await Customers.findMany();',
|
|
530
|
+
" return integrations.get('team');",
|
|
531
|
+
'};',
|
|
532
|
+
].join('\n'), 'export const Customers = { findMany: async () => [] };')).toEqual([]);
|
|
533
|
+
});
|
|
534
|
+
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zitejs",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.124",
|
|
4
4
|
"description": "The Zite framework — build apps on Zite Database",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"main": "./dist/cjs/index.js",
|
|
7
8
|
"module": "./dist/esm/index.js",
|
|
8
9
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -74,6 +75,11 @@
|
|
|
74
75
|
"import": "./dist/esm/schedules/index.js",
|
|
75
76
|
"require": "./dist/cjs/schedules/index.js"
|
|
76
77
|
},
|
|
78
|
+
"./integrations": {
|
|
79
|
+
"types": "./dist/esm/integrations/index.d.ts",
|
|
80
|
+
"import": "./dist/esm/integrations/index.js",
|
|
81
|
+
"require": "./dist/cjs/integrations/index.js"
|
|
82
|
+
},
|
|
77
83
|
"./sync": {
|
|
78
84
|
"types": "./dist/esm/sync/index.d.ts",
|
|
79
85
|
"import": "./dist/esm/sync/index.js",
|
|
@@ -131,6 +137,9 @@
|
|
|
131
137
|
"schedules": [
|
|
132
138
|
"dist/esm/schedules/index.d.ts"
|
|
133
139
|
],
|
|
140
|
+
"integrations": [
|
|
141
|
+
"dist/esm/integrations/index.d.ts"
|
|
142
|
+
],
|
|
134
143
|
"vite-plugin": [
|
|
135
144
|
"dist/esm/vite/index.d.ts"
|
|
136
145
|
]
|
package/dist/cjs/meta/index.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export type ZiteProjectUser = {
|
|
2
|
-
uuid: string;
|
|
3
|
-
firstName: string | null;
|
|
4
|
-
lastName: string | null;
|
|
5
|
-
email: string;
|
|
6
|
-
profilePictureUrl: string | null;
|
|
7
|
-
};
|
|
8
|
-
export type MetaListUsersResult = {
|
|
9
|
-
users: ZiteProjectUser[];
|
|
10
|
-
};
|
|
11
|
-
export declare class ZiteMeta {
|
|
12
|
-
static listUsers(): Promise<MetaListUsersResult>;
|
|
13
|
-
}
|
|
14
|
-
export declare const Meta: typeof ZiteMeta;
|
package/dist/cjs/meta/index.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Meta = exports.ZiteMeta = void 0;
|
|
4
|
-
const sdkCall_js_1 = require("../internal/sdkCall.js");
|
|
5
|
-
const META_SDK_INTEGRATION_ID = '__meta__';
|
|
6
|
-
class ZiteMeta {
|
|
7
|
-
static listUsers() {
|
|
8
|
-
return (0, sdkCall_js_1.getSdkCall)()(META_SDK_INTEGRATION_ID, 'ZiteMeta', 'listUsers', {});
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
exports.ZiteMeta = ZiteMeta;
|
|
12
|
-
exports.Meta = ZiteMeta;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export interface NotificationLink {
|
|
2
|
-
path?: string;
|
|
3
|
-
params?: Record<string, string>;
|
|
4
|
-
}
|
|
5
|
-
export type NotificationsCreateParams = {
|
|
6
|
-
recipients: string[];
|
|
7
|
-
title: string;
|
|
8
|
-
body?: string;
|
|
9
|
-
link?: NotificationLink;
|
|
10
|
-
path?: string;
|
|
11
|
-
params?: Record<string, string>;
|
|
12
|
-
payload?: Record<string, unknown>;
|
|
13
|
-
idempotencyKey?: string;
|
|
14
|
-
};
|
|
15
|
-
export type NotificationsCreateResult = {
|
|
16
|
-
created: number;
|
|
17
|
-
} | {
|
|
18
|
-
created: 0;
|
|
19
|
-
preview: true;
|
|
20
|
-
wouldCreate: number;
|
|
21
|
-
};
|
|
22
|
-
export declare class ZiteNotifications {
|
|
23
|
-
static create(params: NotificationsCreateParams): Promise<NotificationsCreateResult>;
|
|
24
|
-
}
|
|
25
|
-
export declare const Notifications: typeof ZiteNotifications;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Notifications = exports.ZiteNotifications = void 0;
|
|
4
|
-
const sdkCall_js_1 = require("../internal/sdkCall.js");
|
|
5
|
-
const NOTIFICATIONS_SDK_INTEGRATION_ID = '__notifications__';
|
|
6
|
-
class ZiteNotifications {
|
|
7
|
-
static create(params) {
|
|
8
|
-
return (0, sdkCall_js_1.getSdkCall)()(NOTIFICATIONS_SDK_INTEGRATION_ID, 'ZiteNotifications', 'create', params);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
exports.ZiteNotifications = ZiteNotifications;
|
|
12
|
-
exports.Notifications = ZiteNotifications;
|
package/dist/esm/meta/index.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export type ZiteProjectUser = {
|
|
2
|
-
uuid: string;
|
|
3
|
-
firstName: string | null;
|
|
4
|
-
lastName: string | null;
|
|
5
|
-
email: string;
|
|
6
|
-
profilePictureUrl: string | null;
|
|
7
|
-
};
|
|
8
|
-
export type MetaListUsersResult = {
|
|
9
|
-
users: ZiteProjectUser[];
|
|
10
|
-
};
|
|
11
|
-
export declare class ZiteMeta {
|
|
12
|
-
static listUsers(): Promise<MetaListUsersResult>;
|
|
13
|
-
}
|
|
14
|
-
export declare const Meta: typeof ZiteMeta;
|
package/dist/esm/meta/index.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export interface NotificationLink {
|
|
2
|
-
path?: string;
|
|
3
|
-
params?: Record<string, string>;
|
|
4
|
-
}
|
|
5
|
-
export type NotificationsCreateParams = {
|
|
6
|
-
recipients: string[];
|
|
7
|
-
title: string;
|
|
8
|
-
body?: string;
|
|
9
|
-
link?: NotificationLink;
|
|
10
|
-
path?: string;
|
|
11
|
-
params?: Record<string, string>;
|
|
12
|
-
payload?: Record<string, unknown>;
|
|
13
|
-
idempotencyKey?: string;
|
|
14
|
-
};
|
|
15
|
-
export type NotificationsCreateResult = {
|
|
16
|
-
created: number;
|
|
17
|
-
} | {
|
|
18
|
-
created: 0;
|
|
19
|
-
preview: true;
|
|
20
|
-
wouldCreate: number;
|
|
21
|
-
};
|
|
22
|
-
export declare class ZiteNotifications {
|
|
23
|
-
static create(params: NotificationsCreateParams): Promise<NotificationsCreateResult>;
|
|
24
|
-
}
|
|
25
|
-
export declare const Notifications: typeof ZiteNotifications;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { getSdkCall } from '../internal/sdkCall.js';
|
|
2
|
-
const NOTIFICATIONS_SDK_INTEGRATION_ID = '__notifications__';
|
|
3
|
-
export class ZiteNotifications {
|
|
4
|
-
static create(params) {
|
|
5
|
-
return getSdkCall()(NOTIFICATIONS_SDK_INTEGRATION_ID, 'ZiteNotifications', 'create', params);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export const Notifications = ZiteNotifications;
|