jitsu-cli 1.10.4 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/bin/jitsu +3 -0
  2. package/build.mts +39 -0
  3. package/compiled/package.json +27 -29
  4. package/compiled/src/commands/build.js +43 -55
  5. package/compiled/src/commands/config/handlers.js +292 -0
  6. package/compiled/src/commands/config/index.js +148 -0
  7. package/compiled/src/commands/config/resources.js +88 -0
  8. package/compiled/src/commands/default-workspace.js +39 -0
  9. package/compiled/src/commands/deploy.js +127 -78
  10. package/compiled/src/commands/init.js +17 -21
  11. package/compiled/src/commands/login.js +24 -27
  12. package/compiled/src/commands/shared.js +12 -17
  13. package/compiled/src/commands/spec.js +31 -0
  14. package/compiled/src/commands/test.js +7 -10
  15. package/compiled/src/commands/whoami.js +8 -12
  16. package/compiled/src/index.js +40 -24
  17. package/compiled/src/lib/api-client.js +52 -0
  18. package/compiled/src/lib/auth-file.js +67 -0
  19. package/compiled/src/lib/body-builder.js +53 -0
  20. package/compiled/src/lib/body-fields.js +47 -0
  21. package/compiled/src/lib/chalk-code-highlight.js +15 -20
  22. package/compiled/src/lib/compiled-function.js +27 -29
  23. package/compiled/src/lib/dotted.js +34 -0
  24. package/compiled/src/lib/indent.js +3 -8
  25. package/compiled/src/lib/project-config.js +20 -0
  26. package/compiled/src/lib/renderer.js +33 -0
  27. package/compiled/src/lib/spec.js +11 -0
  28. package/compiled/src/lib/template.js +7 -11
  29. package/compiled/src/lib/version.js +13 -20
  30. package/compiled/src/templates/functions.js +13 -18
  31. package/dist/main.js +45496 -86270
  32. package/dist/main.js.map +7 -1
  33. package/package.json +27 -29
  34. package/src/commands/build.ts +22 -27
  35. package/src/commands/config/handlers.ts +339 -0
  36. package/src/commands/config/index.ts +171 -0
  37. package/src/commands/config/resources.ts +110 -0
  38. package/src/commands/default-workspace.ts +44 -0
  39. package/src/commands/deploy.ts +145 -44
  40. package/src/commands/login.ts +3 -1
  41. package/src/commands/spec.ts +32 -0
  42. package/src/index.ts +36 -19
  43. package/src/lib/api-client.ts +64 -0
  44. package/src/lib/auth-file.ts +83 -0
  45. package/src/lib/body-builder.ts +68 -0
  46. package/src/lib/body-fields.ts +61 -0
  47. package/src/lib/compiled-function.ts +30 -23
  48. package/src/lib/dotted.ts +43 -0
  49. package/src/lib/project-config.ts +32 -0
  50. package/src/lib/renderer.ts +44 -0
  51. package/src/lib/spec.ts +32 -0
  52. package/tsconfig.json +2 -19
  53. package/.turbo/turbo-build.log +0 -28
  54. package/.turbo/turbo-clean.log +0 -5
  55. package/babel.config.cjs +0 -4
  56. package/dist/140.js +0 -452
  57. package/dist/140.js.map +0 -1
  58. package/dist/233.js +0 -4890
  59. package/dist/233.js.map +0 -1
  60. package/dist/445e7f36f8a19c2bf682.js +0 -900
  61. package/webpack.config.js +0 -49
@@ -0,0 +1,148 @@
1
+ import { Command } from "commander";
2
+ import { red } from "../../lib/chalk-code-highlight";
3
+ import { DEFAULT_OUTPUT, SUPPORTED_OUTPUTS } from "../../lib/renderer";
4
+ import { resources, verbsFor } from "./resources";
5
+ import { runCreate, runDelete, runGet, runList, runTest, runUpdate } from "./handlers";
6
+ function withCommonOpts(cmd) {
7
+ return cmd
8
+ .option("-w, --workspace <id-or-slug>", "Target workspace id or slug")
9
+ .option("-o, --output <format>", `Output format: ${SUPPORTED_OUTPUTS.join(", ")}`, DEFAULT_OUTPUT)
10
+ .option("-h, --host <host>", "Jitsu host (overrides ~/.jitsu/jitsu-cli.json)")
11
+ .option("-k, --apikey <api-key>", "API key in form keyId:secret (overrides ~/.jitsu/jitsu-cli.json)");
12
+ }
13
+ function withBodyOpts(cmd) {
14
+ return cmd
15
+ .option("-f, --file <path>", "Read body from yaml or json file (use `-` for stdin)")
16
+ .option("--json <json>", "Inline JSON body")
17
+ .addHelpText("after", [
18
+ "",
19
+ "Body fields can also be set ad-hoc via --<path>=<value> flags.",
20
+ "Examples:",
21
+ " --name=my-destination",
22
+ " --destinationType=postgres",
23
+ " --credentials.host=db.example.com",
24
+ " --credentials.password=secret",
25
+ ' --credentials.keys=\'["a","b"]\'',
26
+ 'Values starting with [, {, " or matching number/boolean/null are parsed as JSON;',
27
+ "everything else is a plain string. -f, --json, and --field flags merge in that order.",
28
+ ].join("\n"));
29
+ }
30
+ function action(fn) {
31
+ return async (...args) => {
32
+ try {
33
+ await fn(...args);
34
+ }
35
+ catch (e) {
36
+ const msg = e instanceof Error ? e.message : String(e);
37
+ console.error(red(`Error: ${msg}`));
38
+ process.exit(1);
39
+ }
40
+ };
41
+ }
42
+ function buildLeaf(resource, verb) {
43
+ switch (verb) {
44
+ case "list": {
45
+ const cmd = withCommonOpts(new Command("list"))
46
+ .description(`List ${resource.noun}`)
47
+ .action(action(async (opts) => runList(resource, opts)));
48
+ return cmd;
49
+ }
50
+ case "get": {
51
+ const idLabel = resource.kind === "workspace" ? "<id-or-slug>" : "<id>";
52
+ const cmd = withCommonOpts(new Command("get"))
53
+ .description(`Get a single ${singular(resource)} by id`)
54
+ .argument(idLabel, `Identifier of the ${singular(resource)}`)
55
+ .action(action(async (id, opts) => runGet(resource, id, opts)));
56
+ return cmd;
57
+ }
58
+ case "create": {
59
+ const cmd = withBodyOpts(withCommonOpts(new Command("create")))
60
+ .description(`Create a ${singular(resource)}`)
61
+ .action(action(async (opts) => runCreate(resource, opts)));
62
+ return cmd;
63
+ }
64
+ case "update": {
65
+ const idLabel = resource.kind === "link" ? "[id]" : "<id>";
66
+ const idDesc = resource.kind === "link"
67
+ ? `Link id (optional — connections are upserts identified by fromId+toId)`
68
+ : `Identifier of the ${singular(resource)}`;
69
+ const cmd = withBodyOpts(withCommonOpts(new Command("update")))
70
+ .description(`Update a ${singular(resource)} (deep-merge into existing)`)
71
+ .argument(idLabel, idDesc)
72
+ .action(action(async (id, opts) => runUpdate(resource, id, opts)));
73
+ return cmd;
74
+ }
75
+ case "delete": {
76
+ const idLabel = resource.kind === "link" ? "[id]" : "<id>";
77
+ let cmd = withCommonOpts(new Command("delete"))
78
+ .alias("rm")
79
+ .description(`Delete a ${singular(resource)}`)
80
+ .argument(idLabel, `Identifier of the ${singular(resource)}`);
81
+ if (resource.kind === "configObject") {
82
+ cmd = cmd
83
+ .option("--cascade", "Also delete linked connections that reference this object")
84
+ .option("--strict", "Refuse to delete if linked connections exist");
85
+ }
86
+ if (resource.kind === "link") {
87
+ cmd = cmd
88
+ .option("--from <streamOrServiceId>", "Source id (use with --to instead of <id>)")
89
+ .option("--to <destinationId>", "Destination id (use with --from instead of <id>)");
90
+ }
91
+ cmd = cmd.action(action(async (id, opts) => runDelete(resource, id, opts)));
92
+ return cmd;
93
+ }
94
+ case "test": {
95
+ const cmd = withBodyOpts(withCommonOpts(new Command("test")))
96
+ .description(`Test connectivity for a ${singular(resource)} configuration`)
97
+ .action(action(async (opts) => runTest(resource, opts)));
98
+ return cmd;
99
+ }
100
+ }
101
+ }
102
+ function singular(r) {
103
+ return r.aliases[0] ?? r.noun.replace(/s$/, "");
104
+ }
105
+ export function buildConfigCommand() {
106
+ const config = new Command("config")
107
+ .description("Manage workspace configuration objects (destinations, streams, connections, ...)")
108
+ .addHelpText("after", [
109
+ "",
110
+ "Two equivalent invocation styles are supported:",
111
+ " jitsu config <noun> <verb> [args] e.g. jitsu config destinations list -w ws",
112
+ " jitsu config <verb> <noun> [args] e.g. jitsu config list destinations -w ws",
113
+ "",
114
+ "Resources:",
115
+ ...resources.map(r => ` ${r.noun.padEnd(20)} ${r.description}`),
116
+ ].join("\n"));
117
+ for (const resource of resources) {
118
+ const nounCmd = new Command(resource.noun).description(resource.description);
119
+ for (const alias of resource.aliases)
120
+ nounCmd.alias(alias);
121
+ for (const verb of verbsFor(resource.kind)) {
122
+ nounCmd.addCommand(buildLeaf(resource, verb));
123
+ }
124
+ if (resource.supportsTest) {
125
+ nounCmd.addCommand(buildLeaf(resource, "test"));
126
+ }
127
+ config.addCommand(nounCmd);
128
+ }
129
+ const allVerbs = ["list", "get", "create", "update", "delete", "test"];
130
+ for (const verb of allVerbs) {
131
+ const verbCmd = new Command(verb).description(`${capitalize(verb)} a configuration object (alias for the noun-first form)`);
132
+ for (const resource of resources) {
133
+ const applicable = verbsFor(resource.kind).includes(verb) || (verb === "test" && resource.supportsTest);
134
+ if (!applicable)
135
+ continue;
136
+ const leaf = buildLeaf(resource, verb);
137
+ leaf.name(resource.noun);
138
+ for (const alias of resource.aliases)
139
+ leaf.alias(alias);
140
+ verbCmd.addCommand(leaf);
141
+ }
142
+ config.addCommand(verbCmd);
143
+ }
144
+ return config;
145
+ }
146
+ function capitalize(s) {
147
+ return s.charAt(0).toUpperCase() + s.slice(1);
148
+ }
@@ -0,0 +1,88 @@
1
+ export const resources = [
2
+ {
3
+ noun: "workspaces",
4
+ aliases: ["workspace"],
5
+ kind: "workspace",
6
+ description: "Workspaces accessible to the current user",
7
+ },
8
+ {
9
+ noun: "destinations",
10
+ aliases: ["destination", "dest"],
11
+ kind: "configObject",
12
+ type: "destination",
13
+ supportsTest: true,
14
+ description: "Destinations (warehouses, databases, services receiving events)",
15
+ },
16
+ {
17
+ noun: "streams",
18
+ aliases: ["stream"],
19
+ kind: "configObject",
20
+ type: "stream",
21
+ supportsTest: true,
22
+ description: "Event streams (formerly known as sources)",
23
+ },
24
+ {
25
+ noun: "functions",
26
+ aliases: ["function", "fn"],
27
+ kind: "configObject",
28
+ type: "function",
29
+ description: "User-defined functions (UDFs). For dev workflow see `jitsu deploy`.",
30
+ },
31
+ {
32
+ noun: "services",
33
+ aliases: ["service"],
34
+ kind: "configObject",
35
+ type: "service",
36
+ supportsTest: true,
37
+ description: "External connector services (Airbyte protocol)",
38
+ },
39
+ {
40
+ noun: "domains",
41
+ aliases: ["domain"],
42
+ kind: "configObject",
43
+ type: "domain",
44
+ description: "Custom ingestion domains",
45
+ },
46
+ {
47
+ noun: "misc",
48
+ aliases: [],
49
+ kind: "configObject",
50
+ type: "misc",
51
+ description: "Miscellaneous configuration entities (free-form)",
52
+ },
53
+ {
54
+ noun: "notifications",
55
+ aliases: ["notification"],
56
+ kind: "configObject",
57
+ type: "notification",
58
+ description: "Alert channels (email/Slack)",
59
+ },
60
+ {
61
+ noun: "connections",
62
+ aliases: ["connection", "link", "links"],
63
+ kind: "link",
64
+ description: "Connections between streams/services and destinations",
65
+ },
66
+ {
67
+ noun: "profile-builders",
68
+ aliases: ["profile-builder"],
69
+ kind: "profile-builder",
70
+ description: "Profile builders (identity stitching)",
71
+ },
72
+ ];
73
+ export function verbsFor(kind) {
74
+ switch (kind) {
75
+ case "configObject":
76
+ return ["list", "get", "create", "update", "delete"];
77
+ case "workspace":
78
+ return ["list", "get", "create", "update", "delete"];
79
+ case "link":
80
+ return ["list", "create", "update", "delete"];
81
+ case "profile-builder":
82
+ return ["list", "create", "update", "delete"];
83
+ }
84
+ }
85
+ export function findResource(name) {
86
+ const lower = name.toLowerCase();
87
+ return resources.find(r => r.noun === lower || r.aliases.includes(lower));
88
+ }
@@ -0,0 +1,39 @@
1
+ import { ApiClient, ApiError } from "../lib/api-client";
2
+ import { authFilePath, readAuthFile, resolveAuth, updateAuthFile } from "../lib/auth-file";
3
+ import { red } from "../lib/chalk-code-highlight";
4
+ export async function setDefaultWorkspace(idOrSlug, opts) {
5
+ try {
6
+ const auth = resolveAuth(opts);
7
+ const client = new ApiClient(auth);
8
+ let workspace;
9
+ try {
10
+ workspace = await client.request({
11
+ method: "GET",
12
+ path: `/api/workspace/${encodeURIComponent(idOrSlug)}`,
13
+ });
14
+ }
15
+ catch (e) {
16
+ if (e instanceof ApiError && e.status === 404) {
17
+ throw new Error(`Workspace '${idOrSlug}' not found (or you don't have access)`);
18
+ }
19
+ throw e;
20
+ }
21
+ updateAuthFile({ defaultWorkspace: workspace.id });
22
+ const label = workspace.slug ? `${workspace.slug} (${workspace.id})` : workspace.id;
23
+ console.log(`Default workspace set to ${label}.`);
24
+ console.log(`Saved to ${authFilePath()}.`);
25
+ }
26
+ catch (e) {
27
+ console.error(red(`Error: ${e instanceof Error ? e.message : String(e)}`));
28
+ process.exit(1);
29
+ }
30
+ }
31
+ export async function unsetDefaultWorkspace() {
32
+ const file = readAuthFile();
33
+ if (!file?.defaultWorkspace) {
34
+ console.log("No default workspace is set.");
35
+ return;
36
+ }
37
+ updateAuthFile({ defaultWorkspace: undefined });
38
+ console.log("Default workspace unset.");
39
+ }
@@ -1,68 +1,80 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deploy = deploy;
4
- const tslib_1 = require("tslib");
5
- const path_1 = tslib_1.__importDefault(require("path"));
6
- const os_1 = require("os");
7
- const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
8
- const fs_1 = require("fs");
9
- const shared_1 = require("./shared");
10
- const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
11
- const cuid_1 = tslib_1.__importDefault(require("cuid"));
12
- const chalk_code_highlight_1 = require("../lib/chalk-code-highlight");
13
- const compiled_function_1 = require("../lib/compiled-function");
1
+ import path from "path";
2
+ import { homedir } from "os";
3
+ import inquirer from "inquirer";
4
+ import { existsSync, readdirSync, readFileSync } from "fs";
5
+ import { loadPackageJson } from "./shared";
6
+ import cuid from "cuid";
7
+ import { b, green, red } from "../lib/chalk-code-highlight";
8
+ import { getFunctionFromFilePath } from "../lib/compiled-function";
9
+ import { loadProjectConfig } from "../lib/project-config";
10
+ import { readDefaultWorkspace } from "../lib/auth-file";
14
11
  function readLoginFile() {
15
- const configFile = `${(0, os_1.homedir)()}/.jitsu/jitsu-cli.json`;
16
- if (!(0, fs_1.existsSync)(configFile)) {
17
- console.error((0, chalk_code_highlight_1.red)("Please login first with `jitsu-cli login` command or provide --apikey option"));
12
+ const configFile = `${homedir()}/.jitsu/jitsu-cli.json`;
13
+ if (!existsSync(configFile)) {
14
+ console.error(red("Please login first with `jitsu-cli login` command or provide --apikey option"));
18
15
  process.exit(1);
19
16
  }
20
- return JSON.parse((0, fs_1.readFileSync)(configFile, { encoding: "utf-8" }));
17
+ return JSON.parse(readFileSync(configFile, { encoding: "utf-8" }));
21
18
  }
22
- async function deploy({ dir, workspace, name: names, ...params }) {
23
- const { packageJson, projectDir } = await (0, shared_1.loadPackageJson)(dir || process.cwd());
19
+ export async function deploy({ dir, workspace, name: names, ...params }) {
20
+ const { packageJson, projectDir } = await loadPackageJson(dir || process.cwd());
24
21
  const { host, apikey } = params.apikey
25
22
  ? { apikey: params.apikey, host: params.host || "https://use.jitsu.com" }
26
23
  : readLoginFile();
27
- console.log(`Deploying ${(0, chalk_code_highlight_1.b)(packageJson.name)} project.${names && names.length > 0 ? ` (selected functions: ${names.join(",")})` : ""}`);
28
- const res = await (0, node_fetch_1.default)(`${host}/api/workspace`, {
24
+ console.log(`Deploying ${b(packageJson.name)} project.${names && names.length > 0 ? ` (selected functions: ${names.join(",")})` : ""}`);
25
+ const res = await fetch(`${host}/api/workspace`, {
29
26
  method: "GET",
30
27
  headers: {
31
28
  Authorization: `Bearer ${apikey}`,
32
29
  },
33
30
  });
34
31
  if (!res.ok) {
35
- console.error((0, chalk_code_highlight_1.red)(`Cannot get workspace list:\n${(0, chalk_code_highlight_1.b)(await res.text())}`));
32
+ console.error(red(`Cannot get workspace list:\n${b(await res.text())}`));
36
33
  process.exit(1);
37
34
  }
38
35
  const workspaces = (await res.json());
39
- let workspaceId = workspace;
40
- if (!workspace) {
41
- if (workspaces.length === 0) {
42
- console.error(`${(0, chalk_code_highlight_1.red)("No workspaces found")}`);
36
+ const findWorkspace = (ref) => (ref ? workspaces.find(w => w.id === ref || w.slug === ref) : undefined);
37
+ const projectConfig = loadProjectConfig(projectDir, packageJson);
38
+ let workspaceObj;
39
+ if (workspace) {
40
+ workspaceObj = findWorkspace(workspace);
41
+ if (!workspaceObj) {
42
+ console.error(red(`Workspace '${b(workspace)}' not found (or you don't have access)`));
43
43
  process.exit(1);
44
44
  }
45
- else if (workspaces.length === 1) {
46
- workspaceId = workspaces[0].id;
47
- }
48
- else {
49
- workspaceId = (await inquirer_1.default.prompt([
50
- {
51
- type: "list",
52
- name: "workspaceId",
53
- message: `Select workspace:`,
54
- choices: workspaces.map(w => ({
55
- name: `${w.name} (${w.id})`,
56
- value: w.id,
57
- })),
58
- },
59
- ])).workspaceId;
45
+ }
46
+ else {
47
+ const softRef = projectConfig.workspace ?? readDefaultWorkspace();
48
+ workspaceObj = findWorkspace(softRef);
49
+ if (!workspaceObj) {
50
+ if (softRef) {
51
+ console.warn(`Configured workspace ${b(softRef)} not found or not accessible. Selecting manually.`);
52
+ }
53
+ if (workspaces.length === 0) {
54
+ console.error(`${red("No workspaces found")}`);
55
+ process.exit(1);
56
+ }
57
+ else if (workspaces.length === 1) {
58
+ workspaceObj = workspaces[0];
59
+ }
60
+ else {
61
+ const workspaceId = (await inquirer.prompt([
62
+ {
63
+ type: "list",
64
+ name: "workspaceId",
65
+ message: `Select workspace:`,
66
+ choices: workspaces.map(w => ({
67
+ name: `${w.name} (${w.id})`,
68
+ value: w.id,
69
+ })),
70
+ },
71
+ ])).workspaceId;
72
+ workspaceObj = findWorkspace(workspaceId);
73
+ }
60
74
  }
61
75
  }
62
- const workspaceObj = workspaces.find(w => w.id === workspaceId);
63
- const workspaceName = workspaceObj?.name;
64
- if (!workspaceId || !workspaceName) {
65
- console.error((0, chalk_code_highlight_1.red)(`Workspace with id ${workspaceId} not found`));
76
+ if (!workspaceObj?.id || !workspaceObj?.name) {
77
+ console.error(red(`Workspace not found`));
66
78
  process.exit(1);
67
79
  }
68
80
  await deployFunctions({ ...params, host, apikey, name: names }, projectDir, packageJson, workspaceObj, "function");
@@ -71,11 +83,15 @@ async function deploy({ dir, workspace, name: names, ...params }) {
71
83
  async function deployFunctions({ host, apikey, name: names }, projectDir, packageJson, workspace, kind) {
72
84
  const selected = names ? names.flatMap(n => n.split(",")).map(n => n.trim()) : undefined;
73
85
  const dir = `dist/${kind}s`;
74
- const functionsDir = path_1.default.resolve(projectDir, dir);
75
- const functionsFiles = (0, fs_1.readdirSync)(functionsDir);
86
+ const functionsDir = path.resolve(projectDir, dir);
87
+ if (!existsSync(functionsDir)) {
88
+ console.warn(`No ${b(dir)} directory found, skipping ${kind}s. Please make sure that you have built the project.`);
89
+ return;
90
+ }
91
+ const functionsFiles = readdirSync(functionsDir);
76
92
  if (functionsFiles.length === 0) {
77
- console.warn((0, chalk_code_highlight_1.red)(`Can't find function files in ${(0, chalk_code_highlight_1.b)(dir)} directory. Please make sure that you have built the project.`));
78
- process.exit(1);
93
+ console.warn(`No ${kind} files found in ${b(dir)}, skipping. Please make sure that you have built the project.`);
94
+ return;
79
95
  }
80
96
  const selectedFiles = [];
81
97
  if (selected) {
@@ -85,7 +101,7 @@ async function deployFunctions({ host, apikey, name: names }, projectDir, packag
85
101
  selectedFiles.push(file);
86
102
  }
87
103
  else {
88
- console.error((0, chalk_code_highlight_1.red)(`Can't find function file ${(0, chalk_code_highlight_1.b)(file)} in ${(0, chalk_code_highlight_1.b)(dir)} directory. Please make sure that you have built the project.`));
104
+ console.error(red(`Can't find function file ${b(file)} in ${b(dir)} directory. Please make sure that you have built the project.`));
89
105
  process.exit(1);
90
106
  }
91
107
  }
@@ -95,49 +111,80 @@ async function deployFunctions({ host, apikey, name: names }, projectDir, packag
95
111
  }
96
112
  let profileBuilders = [];
97
113
  if (kind == "profile") {
98
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/profile-builder`, {
114
+ const res = await fetch(`${host}/api/${workspace.id}/config/profile-builder`, {
99
115
  method: "GET",
100
116
  headers: {
101
117
  Authorization: `Bearer ${apikey}`,
102
118
  },
103
119
  });
104
120
  if (!res.ok) {
105
- console.error((0, chalk_code_highlight_1.red)(`Cannot get profile builders list:\n${(0, chalk_code_highlight_1.b)(await res.text())}`));
121
+ console.error(red(`Cannot get profile builders list:\n${b(await res.text())}`));
106
122
  process.exit(1);
107
123
  }
108
124
  profileBuilders = (await res.json()).profileBuilders;
109
125
  }
126
+ const existingFunctions = await fetchExistingFunctions({ host, apikey, workspaceId: workspace.id });
110
127
  for (const file of selectedFiles) {
111
- console.log(`${(0, chalk_code_highlight_1.b)(`𝑓`)} Deploying function ${(0, chalk_code_highlight_1.b)(path_1.default.basename(file))} to workspace ${workspace.name} (${host}/${workspace.slug || workspace.id})`);
112
- await deployFunction({ host, apikey }, packageJson, workspace, kind, path_1.default.resolve(functionsDir, file), profileBuilders);
128
+ console.log(`${b(`𝑓`)} Deploying function ${b(path.basename(file))} to workspace ${workspace.name} (${host}/${workspace.slug || workspace.id})`);
129
+ await deployFunction(projectDir, { host, apikey }, packageJson, workspace, kind, path.resolve(functionsDir, file), profileBuilders, existingFunctions);
130
+ }
131
+ }
132
+ async function fetchExistingFunctions({ host, apikey, workspaceId, }) {
133
+ const res = await fetch(`${host}/api/${workspaceId}/config/function`, {
134
+ headers: { Authorization: `Bearer ${apikey}` },
135
+ });
136
+ if (!res.ok) {
137
+ console.error(red(`Cannot list existing functions:\n${b(await res.text())}`));
138
+ process.exit(1);
139
+ }
140
+ const { objects } = (await res.json());
141
+ const bySlug = new Map();
142
+ const byId = new Map();
143
+ for (const f of objects) {
144
+ const entry = { id: f.id, slug: f.slug };
145
+ byId.set(f.id, entry);
146
+ if (f.slug)
147
+ bySlug.set(f.slug, entry);
113
148
  }
149
+ return { bySlug, byId };
114
150
  }
115
- async function deployFunction({ host, apikey }, packageJson, workspace, kind, file, profileBuilders = []) {
116
- const code = (0, fs_1.readFileSync)(file, "utf-8");
117
- const wrapped = await (0, compiled_function_1.getFunctionFromFilePath)(file, kind, profileBuilders);
151
+ function cacheAfterCreate(cache, id, slug) {
152
+ const entry = { id, slug };
153
+ cache.byId.set(id, entry);
154
+ if (slug)
155
+ cache.bySlug.set(slug, entry);
156
+ }
157
+ function cacheAfterUpdate(cache, id, newSlug) {
158
+ const entry = cache.byId.get(id);
159
+ if (!entry) {
160
+ cacheAfterCreate(cache, id, newSlug);
161
+ return;
162
+ }
163
+ if (entry.slug && entry.slug !== newSlug) {
164
+ cache.bySlug.delete(entry.slug);
165
+ }
166
+ entry.slug = newSlug;
167
+ if (newSlug)
168
+ cache.bySlug.set(newSlug, entry);
169
+ }
170
+ async function deployFunction(projectDir, { host, apikey }, packageJson, workspace, kind, file, profileBuilders = [], existingFunctions = {
171
+ bySlug: new Map(),
172
+ byId: new Map(),
173
+ }) {
174
+ const code = readFileSync(file, "utf-8");
175
+ const wrapped = await getFunctionFromFilePath(projectDir, file, kind, profileBuilders);
118
176
  const meta = wrapped.meta;
119
177
  if (meta) {
120
178
  console.log(` meta: slug=${meta.slug}, name=${meta.name || "not set"}`);
121
179
  }
122
180
  else {
123
- console.log(`File ${(0, chalk_code_highlight_1.b)(path_1.default.basename(file))} doesn't have function meta information. ${(0, chalk_code_highlight_1.red)("Skipping")}`);
181
+ console.log(`File ${b(path.basename(file))} doesn't have function meta information. ${red("Skipping")}`);
124
182
  return;
125
183
  }
126
184
  let existingFunctionId;
127
185
  if (meta.slug) {
128
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/function`, {
129
- headers: {
130
- Authorization: `Bearer ${apikey}`,
131
- },
132
- });
133
- if (!res.ok) {
134
- console.error((0, chalk_code_highlight_1.red)(`Cannot add function to workspace:\n${(0, chalk_code_highlight_1.b)(await res.text())}`));
135
- process.exit(1);
136
- }
137
- else {
138
- const existing = (await res.json());
139
- existingFunctionId = existing.objects.find(f => f.slug === meta.slug || f.id === meta.id)?.id;
140
- }
186
+ existingFunctionId =
187
+ existingFunctions.bySlug.get(meta.slug)?.id ?? (meta.id ? existingFunctions.byId.get(meta.id)?.id : undefined);
141
188
  }
142
189
  let functionPayload = {};
143
190
  if (kind === "profile") {
@@ -152,8 +199,8 @@ async function deployFunction({ host, apikey }, packageJson, workspace, kind, fi
152
199
  };
153
200
  }
154
201
  if (!existingFunctionId) {
155
- const id = (0, cuid_1.default)();
156
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/function`, {
202
+ const id = cuid();
203
+ const res = await fetch(`${host}/api/${workspace.id}/config/function`, {
157
204
  method: "POST",
158
205
  headers: {
159
206
  Authorization: `Bearer ${apikey}`,
@@ -172,16 +219,17 @@ async function deployFunction({ host, apikey }, packageJson, workspace, kind, fi
172
219
  }),
173
220
  });
174
221
  if (!res.ok) {
175
- console.error((0, chalk_code_highlight_1.red)(`Cannot add function to workspace:\n${(0, chalk_code_highlight_1.b)(await res.text())}`));
222
+ console.error(red(`Cannot add function to workspace:\n${b(await res.text())}`));
176
223
  process.exit(1);
177
224
  }
178
225
  else {
179
- console.log(`Function ${(0, chalk_code_highlight_1.b)(meta.name)} was successfully added to workspace ${workspace.name} with id: ${(0, chalk_code_highlight_1.b)(id)}`);
226
+ cacheAfterCreate(existingFunctions, id, meta.slug);
227
+ console.log(`Function ${b(meta.name)} was successfully added to workspace ${workspace.name} with id: ${b(id)}`);
180
228
  }
181
229
  }
182
230
  else {
183
231
  const id = existingFunctionId;
184
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/function/${id}`, {
232
+ const res = await fetch(`${host}/api/${workspace.id}/config/function/${id}`, {
185
233
  method: "PUT",
186
234
  headers: {
187
235
  Authorization: `Bearer ${apikey}`,
@@ -199,11 +247,12 @@ async function deployFunction({ host, apikey }, packageJson, workspace, kind, fi
199
247
  }),
200
248
  });
201
249
  if (!res.ok) {
202
- console.error((0, chalk_code_highlight_1.red)(`⚠ Cannot deploy function ${(0, chalk_code_highlight_1.b)(meta.slug)}(${id}):\n${(0, chalk_code_highlight_1.b)(await res.text())}`));
250
+ console.error(red(`⚠ Cannot deploy function ${b(meta.slug)}(${id}):\n${b(await res.text())}`));
203
251
  process.exit(1);
204
252
  }
205
253
  else {
206
- console.log(`${(0, chalk_code_highlight_1.green)(`✓`)} ${(0, chalk_code_highlight_1.b)(meta.name)} deployed successfully!`);
254
+ cacheAfterUpdate(existingFunctions, id, meta.slug);
255
+ console.log(`${green(`✓`)} ${b(meta.name)} deployed successfully!`);
207
256
  }
208
257
  }
209
258
  }
@@ -1,46 +1,42 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.init = init;
4
- const tslib_1 = require("tslib");
5
- const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
6
- const path_1 = tslib_1.__importDefault(require("path"));
7
- const chalk_code_highlight_1 = require("../lib/chalk-code-highlight");
8
- const fs = tslib_1.__importStar(require("fs"));
9
- const functions_1 = require("../templates/functions");
10
- const template_1 = require("../lib/template");
11
- const version_1 = require("../lib/version");
12
- async function init(dir, opts) {
1
+ import inquirer from "inquirer";
2
+ import path from "path";
3
+ import { b } from "../lib/chalk-code-highlight";
4
+ import * as fs from "fs";
5
+ import { functionProjectTemplate } from "../templates/functions";
6
+ import { write } from "../lib/template";
7
+ import { jitsuCliVersion } from "../lib/version";
8
+ export async function init(dir, opts) {
13
9
  let projectName;
14
10
  if (dir) {
15
- dir = path_1.default.resolve(dir);
11
+ dir = path.resolve(dir);
16
12
  if (!fs.existsSync(dir)) {
17
13
  fs.mkdirSync(dir);
18
14
  }
19
15
  else if (fs.readdirSync(dir).length > 0) {
20
- const msg = `Directory ${(0, chalk_code_highlight_1.b)(dir)} is not empty, can't create project there`;
16
+ const msg = `Directory ${b(dir)} is not empty, can't create project there`;
21
17
  if (opts?.allowNonEmptyDir) {
22
- console.warn(`Directory ${(0, chalk_code_highlight_1.b)(dir)} is not empty. Will create project there, files may be overwritten`);
18
+ console.warn(`Directory ${b(dir)} is not empty. Will create project there, files may be overwritten`);
23
19
  }
24
20
  else {
25
21
  console.error(msg);
26
22
  process.exit(1);
27
23
  }
28
24
  }
29
- projectName = path_1.default.basename(dir);
25
+ projectName = path.basename(dir);
30
26
  }
31
27
  else {
32
- projectName = (await inquirer_1.default.prompt([
28
+ projectName = (await inquirer.prompt([
33
29
  {
34
30
  type: "input",
35
31
  name: "project",
36
32
  message: `Enter project name. It will be used as a package name and directory name:`,
37
33
  },
38
34
  ])).project;
39
- dir = path_1.default.resolve(projectName);
35
+ dir = path.resolve(projectName);
40
36
  }
41
- (0, template_1.write)(dir, functions_1.functionProjectTemplate, {
37
+ write(dir, functionProjectTemplate, {
42
38
  packageName: projectName,
43
- jitsuVersion: opts?.jitsuVersion || version_1.jitsuCliVersion,
39
+ jitsuVersion: opts?.jitsuVersion || jitsuCliVersion,
44
40
  });
45
- console.log(`Project ${(0, chalk_code_highlight_1.b)(projectName)} created!`);
41
+ console.log(`Project ${b(projectName)} created!`);
46
42
  }