@notation/cli 0.11.1 → 0.13.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 (2) hide show
  1. package/dist/index.js +179 -42
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -6,17 +6,23 @@ import { program } from "commander";
6
6
  // src/compile.ts
7
7
  import chokidar from "chokidar";
8
8
  import { glob } from "glob";
9
- import { log } from "console";
10
9
  import esbuild from "esbuild";
11
10
  import {
12
11
  functionInfraPlugin,
13
12
  functionRuntimePlugin
14
13
  } from "@notation/esbuild-plugins";
15
14
  import { filePaths } from "@notation/core";
16
- async function compile(entryPoint, watch2 = false) {
17
- log(`${watch2 ? "Watching" : "Compiling"} infrastructure`, entryPoint);
15
+
16
+ // src/logger.ts
17
+ var defaultLogger = console;
18
+
19
+ // src/compile.ts
20
+ async function compile(entryPoint, opts = {}) {
21
+ const watch2 = opts.watch ?? false;
22
+ const logger = opts.logger ?? defaultLogger;
23
+ logger.info(`${watch2 ? "Watching" : "Compiling"} infrastructure`, entryPoint);
18
24
  await compileInfra(entryPoint, watch2);
19
- log(`${watch2 ? "Watching" : "Compiling"} functions`);
25
+ logger.info(`${watch2 ? "Watching" : "Compiling"} functions`);
20
26
  const fnEntryPoints = await glob("runtime/**/*.fn.ts");
21
27
  let disposeFnCompiler = await compileFns(fnEntryPoints, watch2);
22
28
  if (!watch2) return;
@@ -72,59 +78,167 @@ async function compileFns(entryPoints, watch2 = false) {
72
78
  }
73
79
 
74
80
  // src/deploy.ts
75
- import { deployApp } from "@notation/core";
76
- async function deploy(entryPoint) {
77
- await compile(entryPoint);
78
- console.log(`Deploying ${entryPoint}`);
81
+ import {
82
+ createLoggerReconcilerSubscriber,
83
+ createNdjsonEventEmitter,
84
+ deployApp
85
+ } from "@notation/core";
86
+ import { randomUUID } from "crypto";
87
+
88
+ // src/stdio.ts
89
+ function redirectStdoutToStderr() {
90
+ const originalWrite = process.stdout.write.bind(process.stdout);
91
+ const write = (line) => {
92
+ originalWrite(line);
93
+ };
94
+ process.stdout.write = ((chunk, ...args) => process.stderr.write(
95
+ chunk,
96
+ ...args
97
+ ));
98
+ return {
99
+ write,
100
+ restore: () => {
101
+ process.stdout.write = originalWrite;
102
+ }
103
+ };
104
+ }
105
+
106
+ // src/deploy.ts
107
+ async function deploy(entryPoint, opts = {}) {
108
+ const logger = opts.logger ?? defaultLogger;
109
+ const emit = opts.json ? createNdjsonEventEmitter(redirectStdoutToStderr().write) : createLoggerReconcilerSubscriber({ logger });
110
+ await compile(entryPoint, { logger });
111
+ logger.info(`Deploying ${entryPoint}`);
112
+ const executionId = opts.executionId ?? randomUUID();
113
+ logger.info(`Execution ID ${executionId}`);
114
+ await deployApp({ entryPoint, emit, executionId });
115
+ }
116
+
117
+ // src/destroy.ts
118
+ import {
119
+ createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber2,
120
+ createNdjsonEventEmitter as createNdjsonEventEmitter2,
121
+ destroyApp
122
+ } from "@notation/core";
123
+ import { randomUUID as randomUUID2 } from "crypto";
124
+ async function destroy(entryPoint, opts = {}) {
125
+ const logger = opts.logger ?? defaultLogger;
126
+ const emit = opts.json ? createNdjsonEventEmitter2(redirectStdoutToStderr().write) : createLoggerReconcilerSubscriber2({ logger });
127
+ await compile(entryPoint, { logger });
128
+ logger.info(`Destroying ${entryPoint}
129
+ `);
130
+ const executionId = opts.executionId ?? randomUUID2();
131
+ logger.info(`Execution ID ${executionId}`);
132
+ await destroyApp({ entryPoint, emit, executionId });
133
+ }
134
+
135
+ // src/plan.ts
136
+ import {
137
+ createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber3,
138
+ planApp
139
+ } from "@notation/core";
140
+ var decisionSymbols = {
141
+ create: "+",
142
+ update: "~",
143
+ "drift-update": "~",
144
+ "drift-recreate": "\xB1",
145
+ "delete-orphan": "-",
146
+ noop: " "
147
+ };
148
+ async function plan(entryPoint, opts = {}) {
149
+ const logger = opts.logger ?? defaultLogger;
150
+ const emit = createLoggerReconcilerSubscriber3({ logger });
151
+ if (opts.json) {
152
+ let result2;
153
+ const { restore } = redirectStdoutToStderr();
154
+ try {
155
+ await compile(entryPoint, { logger });
156
+ result2 = await planApp({
157
+ entryPoint,
158
+ emit
159
+ });
160
+ } finally {
161
+ restore();
162
+ }
163
+ process.stdout.write(`${JSON.stringify(result2, null, 2)}
164
+ `);
165
+ return;
166
+ }
167
+ await compile(entryPoint, { logger });
168
+ logger.info(`Planning ${entryPoint}
169
+ `);
170
+ const result = await planApp({
171
+ entryPoint,
172
+ emit
173
+ });
174
+ printPlanSummary(result, logger);
175
+ }
176
+ function printPlanSummary(result, logger) {
177
+ const changedNodes = result.nodes.filter((node) => node.decision !== "noop");
178
+ for (const node of changedNodes) {
179
+ logger.info(
180
+ `${decisionSymbols[node.decision]} ${node.decision} ${node.type} ${node.id}`
181
+ );
182
+ }
183
+ const count = (decision) => result.nodes.filter((node) => node.decision === decision).length;
184
+ const summary = [
185
+ `${count("create")} to create`,
186
+ `${count("update") + count("drift-update")} to update`,
187
+ `${count("drift-recreate")} to recreate`,
188
+ `${count("delete-orphan")} to delete`,
189
+ `${count("noop")} unchanged`
190
+ ].join(", ");
191
+ logger.info(`${changedNodes.length > 0 ? "\n" : ""}Plan: ${summary}.`);
192
+ }
193
+
194
+ // src/run-with-error-handling.ts
195
+ async function runWithErrorHandling(fn, opts) {
79
196
  try {
80
- await deployApp(entryPoint);
81
- } catch (err) {
82
- if (err.name === "CredentialsProviderError") {
83
- console.log(
197
+ await fn();
198
+ return 0;
199
+ } catch (error) {
200
+ if (error instanceof Error && error.name === "CredentialsProviderError") {
201
+ opts.logger.error(
84
202
  "\nAWS credentials not found.",
85
203
  "\n\nEnsure you have a default profile set up in ~/.aws/credentials.",
86
- "\n\nIf using another profile run AWS_PROFILE=otherProfile notation deploy.\n"
204
+ `
205
+
206
+ If using another profile run AWS_PROFILE=otherProfile notation ${opts.command}.
207
+ `
87
208
  );
88
- process.exit(1);
209
+ return 1;
89
210
  }
90
- console.log(err);
91
- process.exit(1);
211
+ opts.logger.error(error);
212
+ return 1;
92
213
  }
93
214
  }
94
215
 
95
- // src/destroy.ts
96
- import { destroyApp } from "@notation/core";
97
- async function destroy(entryPoint) {
98
- await compile(entryPoint);
99
- await destroyApp(entryPoint);
100
- }
101
-
102
216
  // src/visualise.ts
103
217
  import {
104
218
  createMermaidFlowChart,
105
219
  createMermaidLiveUrl,
106
220
  getResourceGraph
107
221
  } from "@notation/core";
108
- import { log as log2 } from "console";
109
- async function visualise(entryPoint) {
222
+ async function visualise(entryPoint, logger = defaultLogger) {
110
223
  await compileInfra(entryPoint);
111
- await generateGraph(entryPoint);
224
+ await generateGraph(entryPoint, logger);
112
225
  }
113
- async function generateGraph(entryPoint) {
114
- log2(`Generating graph for ${entryPoint}`);
226
+ async function generateGraph(entryPoint, logger = defaultLogger) {
227
+ logger.info(`Generating graph for ${entryPoint}`);
115
228
  const graph = await getResourceGraph(entryPoint);
116
229
  const chart = createMermaidFlowChart(graph.resourceGroups, graph.resources);
117
230
  const chartUrl = createMermaidLiveUrl(chart);
118
- log2("\nGenerated infrastructure chart:\n");
119
- log2(chartUrl);
231
+ logger.info("\nGenerated infrastructure chart:\n");
232
+ logger.info(chartUrl);
120
233
  }
121
234
 
122
235
  // src/watch.ts
123
236
  import chokidar2 from "chokidar";
124
- import { deployApp as deployApp2 } from "@notation/core";
237
+ import { randomUUID as randomUUID3 } from "crypto";
238
+ import { createLoggerReconcilerSubscriber as createLoggerReconcilerSubscriber4, deployApp as deployApp2 } from "@notation/core";
125
239
  var dotFilesRe = /(^|[\/\\])\../;
126
- async function watch(entryPoint) {
127
- await compile(entryPoint, true);
240
+ async function watch(entryPoint, logger = defaultLogger) {
241
+ await compile(entryPoint, { watch: true, logger });
128
242
  const watcher = chokidar2.watch("dist", {
129
243
  ignored: dotFilesRe,
130
244
  persistent: true
@@ -148,14 +262,21 @@ async function watch(entryPoint) {
148
262
  return;
149
263
  }
150
264
  isDeploying = true;
151
- deployApp2(entryPoint, false).then(() => {
265
+ const executionId = randomUUID3();
266
+ logger.info(`Execution ID ${executionId}`);
267
+ deployApp2({
268
+ entryPoint,
269
+ executionId,
270
+ driftDetection: false,
271
+ emit: createLoggerReconcilerSubscriber4({ logger })
272
+ }).then(() => {
152
273
  isDeploying = false;
153
274
  if (deployQueued) {
154
275
  deployQueued = false;
155
276
  triggerDeploy();
156
277
  }
157
278
  }).catch((err) => {
158
- console.error(err);
279
+ logger.error(err);
159
280
  isDeploying = false;
160
281
  });
161
282
  }
@@ -163,17 +284,30 @@ async function watch(entryPoint) {
163
284
 
164
285
  // src/index.ts
165
286
  import { startDashboardServer } from "@notation/dashboard";
287
+ import { NodeDurableRuntime, resolveDeploymentId } from "@notation/core";
166
288
  program.command("compile").argument("<entryPoint>", "entryPoint").description("Compile Notation App").action(async (entryPoint) => {
167
289
  await compile(entryPoint);
168
290
  });
169
- program.command("dashboard").description("Start Notation Dashboard").action(async () => {
170
- await startDashboardServer();
291
+ program.command("dashboard").argument("<entryPoint>", "entryPoint").description("Start Notation Dashboard").action(async (entryPoint) => {
292
+ const runtime = new NodeDurableRuntime({
293
+ deploymentId: resolveDeploymentId(entryPoint)
294
+ });
295
+ await startDashboardServer({ state: runtime.state });
296
+ });
297
+ program.command("deploy").argument("<entryPoint>", "entryPoint").description("Deploy Notation App").option("--json", "stream reconciler events as NDJSON").option("--execution-id <id>", "resume a durable execution").action(async (entryPoint, options) => {
298
+ await deploy(entryPoint, {
299
+ json: options.json,
300
+ executionId: options.executionId
301
+ });
171
302
  });
172
- program.command("deploy").argument("<entryPoint>", "entryPoint").description("Deploy Notation App").action(async (entryPoint) => {
173
- await deploy(entryPoint);
303
+ program.command("destroy").argument("<entryPoint>", "entryPoint").description("Destroy Notation App").option("--json", "stream reconciler events as NDJSON").option("--execution-id <id>", "resume a durable execution").action(async (entryPoint, options) => {
304
+ await destroy(entryPoint, {
305
+ json: options.json,
306
+ executionId: options.executionId
307
+ });
174
308
  });
175
- program.command("destroy").argument("<entryPoint>", "entryPoint").description("Destroy Notation App").action(async (entryPoint) => {
176
- await destroy(entryPoint);
309
+ program.command("plan").argument("<entryPoint>", "entryPoint").description("Plan Notation App").option("--json", "print the plan as JSON").action(async (entryPoint, options) => {
310
+ await plan(entryPoint, { json: options.json });
177
311
  });
178
312
  program.command("viz").argument("<entryPoint>", "entryPoint").description("Visualise Notation App").action(async (entryPoint) => {
179
313
  await visualise(entryPoint);
@@ -181,4 +315,7 @@ program.command("viz").argument("<entryPoint>", "entryPoint").description("Visua
181
315
  program.command("watch").argument("<entryPoint>", "entryPoint").description("Watch Notation App").action(async (entryPoint) => {
182
316
  await watch(entryPoint);
183
317
  });
184
- program.parse(process.argv);
318
+ process.exitCode = await runWithErrorHandling(
319
+ () => program.parseAsync(process.argv),
320
+ { logger: defaultLogger, command: process.argv[2] ?? program.name() }
321
+ );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@notation/cli",
4
- "version": "0.11.1",
4
+ "version": "0.13.0",
5
5
  "files": [
6
6
  "dist",
7
7
  "templates"
@@ -13,10 +13,10 @@
13
13
  "chokidar": "^4.0.3",
14
14
  "commander": "^13.1.0",
15
15
  "esbuild": "^0.25.0",
16
- "glob": "^11.0.1",
17
- "@notation/core": "0.11.1",
18
- "@notation/dashboard": "0.11.1",
19
- "@notation/esbuild-plugins": "0.11.1"
16
+ "glob": "^13.0.6",
17
+ "@notation/dashboard": "0.13.0",
18
+ "@notation/core": "0.13.0",
19
+ "@notation/esbuild-plugins": "0.13.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/which-pm-runs": "^1.0.2"