jitsu-cli 1.10.4 → 2.14.0-beta.101

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 (59) hide show
  1. package/bin/jitsu +3 -0
  2. package/build.mts +39 -0
  3. package/compiled/package.json +26 -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 +90 -58
  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 +39 -23
  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/renderer.js +33 -0
  26. package/compiled/src/lib/spec.js +11 -0
  27. package/compiled/src/lib/template.js +7 -11
  28. package/compiled/src/lib/version.js +13 -20
  29. package/compiled/src/templates/functions.js +13 -18
  30. package/dist/main.js +45727 -86292
  31. package/dist/main.js.map +7 -1
  32. package/package.json +26 -29
  33. package/src/commands/build.ts +22 -27
  34. package/src/commands/config/handlers.ts +339 -0
  35. package/src/commands/config/index.ts +171 -0
  36. package/src/commands/config/resources.ts +110 -0
  37. package/src/commands/default-workspace.ts +44 -0
  38. package/src/commands/deploy.ts +96 -20
  39. package/src/commands/login.ts +3 -1
  40. package/src/commands/spec.ts +32 -0
  41. package/src/index.ts +34 -17
  42. package/src/lib/api-client.ts +64 -0
  43. package/src/lib/auth-file.ts +83 -0
  44. package/src/lib/body-builder.ts +68 -0
  45. package/src/lib/body-fields.ts +61 -0
  46. package/src/lib/compiled-function.ts +30 -23
  47. package/src/lib/dotted.ts +43 -0
  48. package/src/lib/renderer.ts +44 -0
  49. package/src/lib/spec.ts +32 -0
  50. package/tsconfig.json +2 -19
  51. package/.turbo/turbo-build.log +0 -28
  52. package/.turbo/turbo-clean.log +0 -5
  53. package/babel.config.cjs +0 -4
  54. package/dist/140.js +0 -452
  55. package/dist/140.js.map +0 -1
  56. package/dist/233.js +0 -4890
  57. package/dist/233.js.map +0 -1
  58. package/dist/445e7f36f8a19c2bf682.js +0 -900
  59. 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,52 +1,47 @@
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";
14
9
  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"));
10
+ const configFile = `${homedir()}/.jitsu/jitsu-cli.json`;
11
+ if (!existsSync(configFile)) {
12
+ console.error(red("Please login first with `jitsu-cli login` command or provide --apikey option"));
18
13
  process.exit(1);
19
14
  }
20
- return JSON.parse((0, fs_1.readFileSync)(configFile, { encoding: "utf-8" }));
15
+ return JSON.parse(readFileSync(configFile, { encoding: "utf-8" }));
21
16
  }
22
- async function deploy({ dir, workspace, name: names, ...params }) {
23
- const { packageJson, projectDir } = await (0, shared_1.loadPackageJson)(dir || process.cwd());
17
+ export async function deploy({ dir, workspace, name: names, ...params }) {
18
+ const { packageJson, projectDir } = await loadPackageJson(dir || process.cwd());
24
19
  const { host, apikey } = params.apikey
25
20
  ? { apikey: params.apikey, host: params.host || "https://use.jitsu.com" }
26
21
  : 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`, {
22
+ console.log(`Deploying ${b(packageJson.name)} project.${names && names.length > 0 ? ` (selected functions: ${names.join(",")})` : ""}`);
23
+ const res = await fetch(`${host}/api/workspace`, {
29
24
  method: "GET",
30
25
  headers: {
31
26
  Authorization: `Bearer ${apikey}`,
32
27
  },
33
28
  });
34
29
  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())}`));
30
+ console.error(red(`Cannot get workspace list:\n${b(await res.text())}`));
36
31
  process.exit(1);
37
32
  }
38
33
  const workspaces = (await res.json());
39
34
  let workspaceId = workspace;
40
35
  if (!workspace) {
41
36
  if (workspaces.length === 0) {
42
- console.error(`${(0, chalk_code_highlight_1.red)("No workspaces found")}`);
37
+ console.error(`${red("No workspaces found")}`);
43
38
  process.exit(1);
44
39
  }
45
40
  else if (workspaces.length === 1) {
46
41
  workspaceId = workspaces[0].id;
47
42
  }
48
43
  else {
49
- workspaceId = (await inquirer_1.default.prompt([
44
+ workspaceId = (await inquirer.prompt([
50
45
  {
51
46
  type: "list",
52
47
  name: "workspaceId",
@@ -62,7 +57,7 @@ async function deploy({ dir, workspace, name: names, ...params }) {
62
57
  const workspaceObj = workspaces.find(w => w.id === workspaceId);
63
58
  const workspaceName = workspaceObj?.name;
64
59
  if (!workspaceId || !workspaceName) {
65
- console.error((0, chalk_code_highlight_1.red)(`Workspace with id ${workspaceId} not found`));
60
+ console.error(red(`Workspace with id ${workspaceId} not found`));
66
61
  process.exit(1);
67
62
  }
68
63
  await deployFunctions({ ...params, host, apikey, name: names }, projectDir, packageJson, workspaceObj, "function");
@@ -71,11 +66,15 @@ async function deploy({ dir, workspace, name: names, ...params }) {
71
66
  async function deployFunctions({ host, apikey, name: names }, projectDir, packageJson, workspace, kind) {
72
67
  const selected = names ? names.flatMap(n => n.split(",")).map(n => n.trim()) : undefined;
73
68
  const dir = `dist/${kind}s`;
74
- const functionsDir = path_1.default.resolve(projectDir, dir);
75
- const functionsFiles = (0, fs_1.readdirSync)(functionsDir);
69
+ const functionsDir = path.resolve(projectDir, dir);
70
+ if (!existsSync(functionsDir)) {
71
+ console.warn(`No ${b(dir)} directory found, skipping ${kind}s. Please make sure that you have built the project.`);
72
+ return;
73
+ }
74
+ const functionsFiles = readdirSync(functionsDir);
76
75
  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);
76
+ console.warn(`No ${kind} files found in ${b(dir)}, skipping. Please make sure that you have built the project.`);
77
+ return;
79
78
  }
80
79
  const selectedFiles = [];
81
80
  if (selected) {
@@ -85,7 +84,7 @@ async function deployFunctions({ host, apikey, name: names }, projectDir, packag
85
84
  selectedFiles.push(file);
86
85
  }
87
86
  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.`));
87
+ console.error(red(`Can't find function file ${b(file)} in ${b(dir)} directory. Please make sure that you have built the project.`));
89
88
  process.exit(1);
90
89
  }
91
90
  }
@@ -95,49 +94,80 @@ async function deployFunctions({ host, apikey, name: names }, projectDir, packag
95
94
  }
96
95
  let profileBuilders = [];
97
96
  if (kind == "profile") {
98
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/profile-builder`, {
97
+ const res = await fetch(`${host}/api/${workspace.id}/config/profile-builder`, {
99
98
  method: "GET",
100
99
  headers: {
101
100
  Authorization: `Bearer ${apikey}`,
102
101
  },
103
102
  });
104
103
  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())}`));
104
+ console.error(red(`Cannot get profile builders list:\n${b(await res.text())}`));
106
105
  process.exit(1);
107
106
  }
108
107
  profileBuilders = (await res.json()).profileBuilders;
109
108
  }
109
+ const existingFunctions = await fetchExistingFunctions({ host, apikey, workspaceId: workspace.id });
110
110
  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);
111
+ console.log(`${b(`𝑓`)} Deploying function ${b(path.basename(file))} to workspace ${workspace.name} (${host}/${workspace.slug || workspace.id})`);
112
+ await deployFunction(projectDir, { host, apikey }, packageJson, workspace, kind, path.resolve(functionsDir, file), profileBuilders, existingFunctions);
113
+ }
114
+ }
115
+ async function fetchExistingFunctions({ host, apikey, workspaceId, }) {
116
+ const res = await fetch(`${host}/api/${workspaceId}/config/function`, {
117
+ headers: { Authorization: `Bearer ${apikey}` },
118
+ });
119
+ if (!res.ok) {
120
+ console.error(red(`Cannot list existing functions:\n${b(await res.text())}`));
121
+ process.exit(1);
122
+ }
123
+ const { objects } = (await res.json());
124
+ const bySlug = new Map();
125
+ const byId = new Map();
126
+ for (const f of objects) {
127
+ const entry = { id: f.id, slug: f.slug };
128
+ byId.set(f.id, entry);
129
+ if (f.slug)
130
+ bySlug.set(f.slug, entry);
131
+ }
132
+ return { bySlug, byId };
133
+ }
134
+ function cacheAfterCreate(cache, id, slug) {
135
+ const entry = { id, slug };
136
+ cache.byId.set(id, entry);
137
+ if (slug)
138
+ cache.bySlug.set(slug, entry);
139
+ }
140
+ function cacheAfterUpdate(cache, id, newSlug) {
141
+ const entry = cache.byId.get(id);
142
+ if (!entry) {
143
+ cacheAfterCreate(cache, id, newSlug);
144
+ return;
145
+ }
146
+ if (entry.slug && entry.slug !== newSlug) {
147
+ cache.bySlug.delete(entry.slug);
113
148
  }
149
+ entry.slug = newSlug;
150
+ if (newSlug)
151
+ cache.bySlug.set(newSlug, entry);
114
152
  }
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);
153
+ async function deployFunction(projectDir, { host, apikey }, packageJson, workspace, kind, file, profileBuilders = [], existingFunctions = {
154
+ bySlug: new Map(),
155
+ byId: new Map(),
156
+ }) {
157
+ const code = readFileSync(file, "utf-8");
158
+ const wrapped = await getFunctionFromFilePath(projectDir, file, kind, profileBuilders);
118
159
  const meta = wrapped.meta;
119
160
  if (meta) {
120
161
  console.log(` meta: slug=${meta.slug}, name=${meta.name || "not set"}`);
121
162
  }
122
163
  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")}`);
164
+ console.log(`File ${b(path.basename(file))} doesn't have function meta information. ${red("Skipping")}`);
124
165
  return;
125
166
  }
126
167
  let existingFunctionId;
127
168
  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
- }
169
+ existingFunctionId =
170
+ existingFunctions.bySlug.get(meta.slug)?.id ?? (meta.id ? existingFunctions.byId.get(meta.id)?.id : undefined);
141
171
  }
142
172
  let functionPayload = {};
143
173
  if (kind === "profile") {
@@ -152,8 +182,8 @@ async function deployFunction({ host, apikey }, packageJson, workspace, kind, fi
152
182
  };
153
183
  }
154
184
  if (!existingFunctionId) {
155
- const id = (0, cuid_1.default)();
156
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/function`, {
185
+ const id = cuid();
186
+ const res = await fetch(`${host}/api/${workspace.id}/config/function`, {
157
187
  method: "POST",
158
188
  headers: {
159
189
  Authorization: `Bearer ${apikey}`,
@@ -172,16 +202,17 @@ async function deployFunction({ host, apikey }, packageJson, workspace, kind, fi
172
202
  }),
173
203
  });
174
204
  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())}`));
205
+ console.error(red(`Cannot add function to workspace:\n${b(await res.text())}`));
176
206
  process.exit(1);
177
207
  }
178
208
  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)}`);
209
+ cacheAfterCreate(existingFunctions, id, meta.slug);
210
+ console.log(`Function ${b(meta.name)} was successfully added to workspace ${workspace.name} with id: ${b(id)}`);
180
211
  }
181
212
  }
182
213
  else {
183
214
  const id = existingFunctionId;
184
- const res = await (0, node_fetch_1.default)(`${host}/api/${workspace.id}/config/function/${id}`, {
215
+ const res = await fetch(`${host}/api/${workspace.id}/config/function/${id}`, {
185
216
  method: "PUT",
186
217
  headers: {
187
218
  Authorization: `Bearer ${apikey}`,
@@ -199,11 +230,12 @@ async function deployFunction({ host, apikey }, packageJson, workspace, kind, fi
199
230
  }),
200
231
  });
201
232
  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())}`));
233
+ console.error(red(`⚠ Cannot deploy function ${b(meta.slug)}(${id}):\n${b(await res.text())}`));
203
234
  process.exit(1);
204
235
  }
205
236
  else {
206
- console.log(`${(0, chalk_code_highlight_1.green)(`✓`)} ${(0, chalk_code_highlight_1.b)(meta.name)} deployed successfully!`);
237
+ cacheAfterUpdate(existingFunctions, id, meta.slug);
238
+ console.log(`${green(`✓`)} ${b(meta.name)} deployed successfully!`);
207
239
  }
208
240
  }
209
241
  }
@@ -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
  }