@knocklabs/cli 0.3.0 → 1.0.0-rc.1

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 (68) hide show
  1. package/README.md +505 -162
  2. package/dist/commands/branch/create.js +0 -2
  3. package/dist/commands/branch/delete.js +4 -3
  4. package/dist/commands/branch/exit.js +0 -2
  5. package/dist/commands/branch/list.js +0 -2
  6. package/dist/commands/branch/merge.js +82 -0
  7. package/dist/commands/branch/switch.js +0 -2
  8. package/dist/commands/channel/list.js +73 -0
  9. package/dist/commands/environment/list.js +73 -0
  10. package/dist/commands/guide/new.js +276 -0
  11. package/dist/commands/guide/pull.js +5 -6
  12. package/dist/commands/guide/push.js +1 -1
  13. package/dist/commands/guide/validate.js +1 -1
  14. package/dist/commands/init.js +108 -0
  15. package/dist/commands/layout/new.js +228 -0
  16. package/dist/commands/layout/pull.js +5 -6
  17. package/dist/commands/layout/push.js +1 -1
  18. package/dist/commands/layout/validate.js +1 -1
  19. package/dist/commands/message-type/new.js +228 -0
  20. package/dist/commands/message-type/pull.js +5 -6
  21. package/dist/commands/message-type/push.js +1 -1
  22. package/dist/commands/message-type/validate.js +1 -1
  23. package/dist/commands/partial/new.js +274 -0
  24. package/dist/commands/partial/pull.js +5 -6
  25. package/dist/commands/partial/push.js +1 -1
  26. package/dist/commands/partial/validate.js +1 -1
  27. package/dist/commands/pull.js +7 -2
  28. package/dist/commands/push.js +6 -4
  29. package/dist/commands/translation/pull.js +1 -1
  30. package/dist/commands/translation/push.js +1 -1
  31. package/dist/commands/translation/validate.js +1 -1
  32. package/dist/commands/workflow/new.js +179 -54
  33. package/dist/commands/workflow/pull.js +6 -8
  34. package/dist/commands/workflow/push.js +1 -1
  35. package/dist/commands/workflow/validate.js +1 -1
  36. package/dist/lib/api-v1.js +23 -2
  37. package/dist/lib/auth.js +1 -1
  38. package/dist/lib/base-command.js +18 -15
  39. package/dist/lib/helpers/flag.js +0 -2
  40. package/dist/lib/helpers/project-config.js +158 -0
  41. package/dist/lib/helpers/request.js +1 -2
  42. package/dist/lib/helpers/string.js +4 -4
  43. package/dist/lib/helpers/typegen.js +1 -1
  44. package/dist/lib/marshal/email-layout/generator.js +152 -0
  45. package/dist/lib/marshal/email-layout/helpers.js +6 -9
  46. package/dist/lib/marshal/email-layout/index.js +1 -0
  47. package/dist/lib/marshal/email-layout/writer.js +15 -3
  48. package/dist/lib/marshal/guide/generator.js +163 -0
  49. package/dist/lib/marshal/guide/helpers.js +6 -10
  50. package/dist/lib/marshal/guide/index.js +1 -0
  51. package/dist/lib/marshal/guide/writer.js +5 -9
  52. package/dist/lib/marshal/message-type/generator.js +139 -0
  53. package/dist/lib/marshal/message-type/helpers.js +6 -10
  54. package/dist/lib/marshal/message-type/index.js +1 -0
  55. package/dist/lib/marshal/message-type/writer.js +5 -1
  56. package/dist/lib/marshal/partial/generator.js +159 -0
  57. package/dist/lib/marshal/partial/helpers.js +6 -10
  58. package/dist/lib/marshal/partial/index.js +1 -0
  59. package/dist/lib/marshal/partial/writer.js +3 -0
  60. package/dist/lib/marshal/translation/helpers.js +6 -10
  61. package/dist/lib/marshal/translation/processor.isomorphic.js +4 -4
  62. package/dist/lib/marshal/translation/writer.js +2 -2
  63. package/dist/lib/marshal/workflow/generator.js +175 -19
  64. package/dist/lib/marshal/workflow/helpers.js +7 -10
  65. package/dist/lib/run-context/loader.js +5 -0
  66. package/dist/lib/templates.js +131 -0
  67. package/oclif.manifest.json +826 -266
  68. package/package.json +14 -9
@@ -9,9 +9,18 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
+ get StepTagChoices () {
13
+ return StepTagChoices;
14
+ },
12
15
  get generateWorkflowDir () {
13
16
  return generateWorkflowDir;
14
17
  },
18
+ get generateWorkflowFromTemplate () {
19
+ return generateWorkflowFromTemplate;
20
+ },
21
+ get getAvailableStepTypes () {
22
+ return getAvailableStepTypes;
23
+ },
15
24
  get parseStepsInput () {
16
25
  return parseStepsInput;
17
26
  },
@@ -22,7 +31,9 @@ _export(exports, {
22
31
  const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
23
32
  const _lodash = require("lodash");
24
33
  const _constisomorphic = require("../shared/const.isomorphic");
34
+ const _templates = /*#__PURE__*/ _interop_require_wildcard(require("../../templates"));
25
35
  const _processorisomorphic = require("./processor.isomorphic");
36
+ const _reader = require("./reader");
26
37
  const _types = require("./types");
27
38
  const _writer = require("./writer");
28
39
  function _getRequireWildcardCache(nodeInterop) {
@@ -115,13 +126,14 @@ const scaffoldHttpFetchStep = (refSuffix)=>{
115
126
  {}
116
127
  ];
117
128
  };
118
- const scaffoldEmailChannelStep = (refSuffix)=>{
129
+ const scaffoldEmailChannelStep = (refSuffix, channels)=>{
119
130
  const stepRef = `email_${refSuffix}`;
120
131
  const templateFilePath = newTemplateFilePath(stepRef, "html_body", "html");
132
+ const firstChannel = channels[0];
121
133
  const scaffoldedStep = {
122
134
  ref: stepRef,
123
135
  type: _types.StepType.Channel,
124
- channel_key: "<EMAIL CHANNEL KEY>",
136
+ channel_key: firstChannel ? firstChannel.key : "<EMAIL CHANNEL KEY>",
125
137
  template: {
126
138
  settings: {
127
139
  layout_key: "default"
@@ -138,13 +150,14 @@ const scaffoldEmailChannelStep = (refSuffix)=>{
138
150
  bundleFragment
139
151
  ];
140
152
  };
141
- const scaffoldInAppFeedChannelStep = (refSuffix)=>{
153
+ const scaffoldInAppFeedChannelStep = (refSuffix, channels)=>{
142
154
  const stepRef = `in_app_feed_${refSuffix}`;
143
155
  const templateFilePath = newTemplateFilePath(stepRef, "markdown_body", "md");
156
+ const firstChannel = channels[0];
144
157
  const scaffoldedStep = {
145
158
  ref: stepRef,
146
159
  type: _types.StepType.Channel,
147
- channel_key: "<IN-APP-FEED CHANNEL KEY>",
160
+ channel_key: firstChannel ? firstChannel.key : "<IN-APP-FEED CHANNEL KEY>",
148
161
  template: {
149
162
  action_url: "{{ vars.app_url }}",
150
163
  ["markdown_body" + _constisomorphic.FILEPATH_MARKER]: templateFilePath
@@ -158,13 +171,14 @@ const scaffoldInAppFeedChannelStep = (refSuffix)=>{
158
171
  bundleFragment
159
172
  ];
160
173
  };
161
- const scaffoldSmsChannelStep = (refSuffix)=>{
174
+ const scaffoldSmsChannelStep = (refSuffix, channels)=>{
162
175
  const stepRef = `sms_${refSuffix}`;
163
176
  const templateFilePath = newTemplateFilePath(stepRef, "text_body", "txt");
177
+ const firstChannel = channels[0];
164
178
  const scaffoldedStep = {
165
179
  ref: stepRef,
166
180
  type: _types.StepType.Channel,
167
- channel_key: "<SMS CHANNEL KEY>",
181
+ channel_key: firstChannel ? firstChannel.key : "<SMS CHANNEL KEY>",
168
182
  template: {
169
183
  ["text_body" + _constisomorphic.FILEPATH_MARKER]: templateFilePath
170
184
  }
@@ -177,13 +191,14 @@ const scaffoldSmsChannelStep = (refSuffix)=>{
177
191
  bundleFragment
178
192
  ];
179
193
  };
180
- const scaffoldPushChannelStep = (refSuffix)=>{
194
+ const scaffoldPushChannelStep = (refSuffix, channels)=>{
181
195
  const stepRef = `push_${refSuffix}`;
182
196
  const templateFilePath = newTemplateFilePath(stepRef, "text_body", "txt");
197
+ const firstChannel = channels[0];
183
198
  const scaffoldedStep = {
184
199
  ref: stepRef,
185
200
  type: _types.StepType.Channel,
186
- channel_key: "<PUSH CHANNEL KEY>",
201
+ channel_key: firstChannel ? firstChannel.key : "<PUSH CHANNEL KEY>",
187
202
  template: {
188
203
  settings: {
189
204
  delivery_type: "content"
@@ -199,13 +214,14 @@ const scaffoldPushChannelStep = (refSuffix)=>{
199
214
  bundleFragment
200
215
  ];
201
216
  };
202
- const scaffoldChatChannelStep = (refSuffix)=>{
217
+ const scaffoldChatChannelStep = (refSuffix, channels)=>{
203
218
  const stepRef = `chat_${refSuffix}`;
204
219
  const templateFilePath = newTemplateFilePath(stepRef, "markdown_body", "md");
220
+ const firstChannel = channels[0];
205
221
  const scaffoldedStep = {
206
222
  ref: stepRef,
207
223
  type: _types.StepType.Channel,
208
- channel_key: "<CHAT CHANNEL KEY>",
224
+ channel_key: firstChannel ? firstChannel.key : "<CHAT CHANNEL KEY>",
209
225
  template: {
210
226
  ["markdown_body" + _constisomorphic.FILEPATH_MARKER]: templateFilePath
211
227
  }
@@ -223,11 +239,21 @@ const STEP_TAGS = [
223
239
  "batch",
224
240
  "fetch",
225
241
  "email",
226
- "in-app",
242
+ "in-app-feed",
227
243
  "sms",
228
244
  "push",
229
245
  "chat"
230
246
  ];
247
+ const StepTagChoices = {
248
+ delay: "Delay step",
249
+ batch: "Batch step",
250
+ fetch: "HTTP Fetch step",
251
+ email: "Email channel step",
252
+ "in-app-feed": "In-app feed channel step",
253
+ sms: "SMS channel step",
254
+ push: "Push channel step",
255
+ chat: "Chat channel step"
256
+ };
231
257
  const stepScaffoldFuncs = {
232
258
  // Function steps
233
259
  delay: scaffoldDelayStep,
@@ -235,18 +261,18 @@ const stepScaffoldFuncs = {
235
261
  fetch: scaffoldHttpFetchStep,
236
262
  // Channel steps
237
263
  email: scaffoldEmailChannelStep,
238
- "in-app": scaffoldInAppFeedChannelStep,
264
+ "in-app-feed": scaffoldInAppFeedChannelStep,
239
265
  sms: scaffoldSmsChannelStep,
240
266
  push: scaffoldPushChannelStep,
241
267
  chat: scaffoldChatChannelStep
242
268
  };
243
- const parseStepsInput = (input)=>{
269
+ const parseStepsInput = (input, availableStepTypes)=>{
244
270
  const tags = input.split(",").filter((x)=>x);
245
- const invalidTags = tags.filter((tag)=>!(0, _lodash.get)(stepScaffoldFuncs, tag));
271
+ const invalidTags = tags.filter((tag)=>!availableStepTypes.includes(tag));
246
272
  if (invalidTags.length > 0) {
247
273
  return [
248
274
  undefined,
249
- "must be a comma-separated string of the following values: " + STEP_TAGS.join(", ")
275
+ `Invalid step type: ${invalidTags.join(", ")}`
250
276
  ];
251
277
  }
252
278
  return [
@@ -254,7 +280,26 @@ const parseStepsInput = (input)=>{
254
280
  undefined
255
281
  ];
256
282
  };
257
- const scaffoldWorkflowDirBundle = (attrs)=>{
283
+ const getAvailableStepTypes = (channelTypes)=>{
284
+ // Return a list of step
285
+ return STEP_TAGS.filter((stepTag)=>{
286
+ switch(stepTag){
287
+ case "email":
288
+ return channelTypes.includes("email");
289
+ case "in-app-feed":
290
+ return channelTypes.includes("in_app_feed");
291
+ case "sms":
292
+ return channelTypes.includes("sms");
293
+ case "push":
294
+ return channelTypes.includes("push");
295
+ case "chat":
296
+ return channelTypes.includes("chat");
297
+ default:
298
+ return true;
299
+ }
300
+ });
301
+ };
302
+ const scaffoldWorkflowDirBundle = (attrs, channelsByType)=>{
258
303
  // A map of 1-based counters to track the number of each step tag seen, used
259
304
  // for formatting step refs.
260
305
  const stepCountByTag = (0, _lodash.assign)({}, ...STEP_TAGS.map((tag)=>({
@@ -263,7 +308,17 @@ const scaffoldWorkflowDirBundle = (attrs)=>{
263
308
  // Scaffold workflow steps data based on given step tags.
264
309
  const [scaffoldedSteps = [], bundleFragments = []] = (0, _lodash.zip)(...(attrs.steps || []).map((tag)=>{
265
310
  const stepCount = ++stepCountByTag[tag];
266
- return stepScaffoldFuncs[tag](stepCount);
311
+ switch(tag){
312
+ case "email":
313
+ case "sms":
314
+ case "push":
315
+ case "chat":
316
+ return stepScaffoldFuncs[tag](stepCount, (0, _lodash.get)(channelsByType, tag, []));
317
+ case "in-app-feed":
318
+ return stepScaffoldFuncs[tag](stepCount, (0, _lodash.get)(channelsByType, "in_app_feed", []));
319
+ default:
320
+ return stepScaffoldFuncs[tag](stepCount);
321
+ }
267
322
  }));
268
323
  // Build a workflow directory bundle with scaffolded steps data, plus other
269
324
  // workflow attributes.
@@ -275,7 +330,108 @@ const scaffoldWorkflowDirBundle = (attrs)=>{
275
330
  [_processorisomorphic.WORKFLOW_JSON]: workflowJson
276
331
  }, ...bundleFragments);
277
332
  };
278
- const generateWorkflowDir = async (workflowDirCtx, attrs)=>{
279
- const bundle = scaffoldWorkflowDirBundle(attrs);
333
+ const generateWorkflowDir = async (workflowDirCtx, attrs, channelsByType)=>{
334
+ const bundle = scaffoldWorkflowDirBundle(attrs, channelsByType);
280
335
  return (0, _writer.writeWorkflowDirFromBundle)(workflowDirCtx, bundle);
281
336
  };
337
+ function swapChannelReferences(step, channelsByType) {
338
+ // Temporary until we start shipping the `channel_type` field on channel steps
339
+ // so that we can then branch by that value.
340
+ switch(step.channel_key){
341
+ case "postmark":
342
+ case "sendgrid":
343
+ case "resend":
344
+ case "email":
345
+ case "knock-email":
346
+ var _channelsByType_email_;
347
+ return {
348
+ ...step,
349
+ channel_key: (_channelsByType_email_ = channelsByType.email[0]) === null || _channelsByType_email_ === void 0 ? void 0 : _channelsByType_email_.key
350
+ };
351
+ case "in-app-feed":
352
+ case "knock-in-app-feed":
353
+ case "knock-feed":
354
+ var _channelsByType_in_app_feed_;
355
+ return {
356
+ ...step,
357
+ channel_key: (_channelsByType_in_app_feed_ = channelsByType.in_app_feed[0]) === null || _channelsByType_in_app_feed_ === void 0 ? void 0 : _channelsByType_in_app_feed_.key
358
+ };
359
+ case "sms":
360
+ case "knock-sms":
361
+ case "twilio":
362
+ var _channelsByType_sms_;
363
+ return {
364
+ ...step,
365
+ channel_key: (_channelsByType_sms_ = channelsByType.sms[0]) === null || _channelsByType_sms_ === void 0 ? void 0 : _channelsByType_sms_.key
366
+ };
367
+ case "push":
368
+ case "apns":
369
+ case "fcm":
370
+ var _channelsByType_push_;
371
+ return {
372
+ ...step,
373
+ channel_key: (_channelsByType_push_ = channelsByType.push[0]) === null || _channelsByType_push_ === void 0 ? void 0 : _channelsByType_push_.key
374
+ };
375
+ case "chat":
376
+ var _channelsByType_chat_;
377
+ return {
378
+ ...step,
379
+ channel_key: (_channelsByType_chat_ = channelsByType.chat[0]) === null || _channelsByType_chat_ === void 0 ? void 0 : _channelsByType_chat_.key
380
+ };
381
+ default:
382
+ throw new Error(`Unknown channel type: ${step.channel_key}`);
383
+ }
384
+ }
385
+ function walkWorkflowSteps(steps, channelsByType) {
386
+ return steps.map((step)=>{
387
+ switch(step.type){
388
+ case _types.StepType.Channel:
389
+ return swapChannelReferences(step, channelsByType);
390
+ case _types.StepType.Branch:
391
+ var _step_branches;
392
+ return {
393
+ ...step,
394
+ branches: ((_step_branches = step.branches) !== null && _step_branches !== void 0 ? _step_branches : []).map((branch)=>{
395
+ var _branch_steps;
396
+ return {
397
+ ...branch,
398
+ steps: walkWorkflowSteps((_branch_steps = branch.steps) !== null && _branch_steps !== void 0 ? _branch_steps : [], channelsByType)
399
+ };
400
+ })
401
+ };
402
+ default:
403
+ return step;
404
+ }
405
+ });
406
+ }
407
+ const generateWorkflowFromTemplate = async (workflowDirCtx, templateString, attrs, channelsByType)=>{
408
+ let tempDir;
409
+ try {
410
+ var _workflow_steps;
411
+ // Download the template directory into a temp directory
412
+ tempDir = await _templates.downloadTemplate(templateString);
413
+ // Create a workflow directory context for the temp directory
414
+ const tempWorkflowDirCtx = {
415
+ type: "workflow",
416
+ key: "temp",
417
+ abspath: tempDir,
418
+ exists: true
419
+ };
420
+ // Read the workflow.json from the temp directory we downloaded
421
+ const [workflow, errors] = await (0, _reader.readWorkflowDir)(tempWorkflowDirCtx, {
422
+ withExtractedFiles: true
423
+ });
424
+ if (errors.length > 0 || !workflow) {
425
+ throw new Error(`Invalid workflow template: ${errors.join(", ")}`);
426
+ }
427
+ // Modify the workflow data with the new attributes
428
+ workflow.name = attrs.name;
429
+ // Parse the workflow steps, and replace any channel steps with the correct `channel_key`
430
+ workflow.steps = walkWorkflowSteps((_workflow_steps = workflow.steps) !== null && _workflow_steps !== void 0 ? _workflow_steps : [], channelsByType);
431
+ // Finally, we write the workflow into the target workflow directory
432
+ (0, _writer.writeWorkflowDirFromData)(workflowDirCtx, workflow);
433
+ return;
434
+ } finally{
435
+ await _templates.cleanupTempDir(tempDir);
436
+ }
437
+ };
@@ -45,6 +45,7 @@ const _core = require("@oclif/core");
45
45
  const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
46
46
  const _lodash = require("lodash");
47
47
  const _quicktypecore = require("quicktype-core");
48
+ const _projectconfig = require("../../helpers/project-config");
48
49
  const _string = require("../../helpers/string");
49
50
  const _typegen = require("../../helpers/typegen");
50
51
  const _processorisomorphic = require("./processor.isomorphic");
@@ -197,7 +198,7 @@ const formatStepSummary = (step)=>{
197
198
  const formatStatus = (workflow)=>{
198
199
  return workflow.active ? "active" : "inactive";
199
200
  };
200
- const ensureValidCommandTarget = async (props, runContext)=>{
201
+ const ensureValidCommandTarget = async (props, runContext, projectConfig)=>{
201
202
  const { args, flags } = props;
202
203
  const { commandId, resourceDir: resourceDirCtx, cwd: runCwd } = runContext;
203
204
  // If the target resource is a different type than the current resource dir
@@ -209,6 +210,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
209
210
  if (flags.all && args.workflowKey) {
210
211
  return _core.ux.error(`workflowKey arg \`${args.workflowKey}\` cannot also be provided when using --all`);
211
212
  }
213
+ // Targeting all workflow dirs in the workflows index dir.
214
+ // Default to knock project config first if present, otherwise cwd.
215
+ const workflowsIndexDirCtx = await (0, _projectconfig.resolveResourceDir)(projectConfig, "workflow", runCwd);
212
216
  // --all flag is given, which means no workflow key arg.
213
217
  if (flags.all) {
214
218
  // If --all flag used inside a workflow directory, then require a workflows
@@ -216,16 +220,9 @@ const ensureValidCommandTarget = async (props, runContext)=>{
216
220
  if (resourceDirCtx && !flags["workflows-dir"]) {
217
221
  return _core.ux.error("Missing required flag workflows-dir");
218
222
  }
219
- // Targeting all workflow dirs in the workflows index dir.
220
- // TODO: Default to the knock project config first if present before cwd.
221
- const defaultToCwd = {
222
- abspath: runCwd,
223
- exists: true
224
- };
225
- const indexDirCtx = flags["workflows-dir"] || defaultToCwd;
226
223
  return {
227
224
  type: "workflowsIndexDir",
228
- context: indexDirCtx
225
+ context: flags["workflows-dir"] || workflowsIndexDirCtx
229
226
  };
230
227
  }
231
228
  // Workflow key arg is given, which means no --all flag.
@@ -233,7 +230,7 @@ const ensureValidCommandTarget = async (props, runContext)=>{
233
230
  if (resourceDirCtx && resourceDirCtx.key !== args.workflowKey) {
234
231
  return _core.ux.error(`Cannot run ${commandId} \`${args.workflowKey}\` inside another workflow directory:\n${resourceDirCtx.key}`);
235
232
  }
236
- const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(runCwd, args.workflowKey);
233
+ const targetDirPath = resourceDirCtx ? resourceDirCtx.abspath : _nodepath.resolve(workflowsIndexDirCtx.abspath, args.workflowKey);
237
234
  const workflowDirCtx = {
238
235
  type: "workflow",
239
236
  key: args.workflowKey,
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "load", {
14
14
  });
15
15
  const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
16
16
  const _emaillayout = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/email-layout"));
17
+ const _guide = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/guide"));
17
18
  const _messagetype = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/message-type"));
18
19
  const _partial = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/partial"));
19
20
  const _translation = /*#__PURE__*/ _interop_require_wildcard(require("../marshal/translation"));
@@ -74,6 +75,10 @@ const evaluateRecursively = async (ctx, currDir)=>{
74
75
  if (isWorkflowDir) {
75
76
  ctx.resourceDir = buildResourceDirContext("workflow", currDir);
76
77
  }
78
+ const isGuideDir = await _guide.isGuideDir(currDir);
79
+ if (isGuideDir) {
80
+ ctx.resourceDir = buildResourceDirContext("guide", currDir);
81
+ }
77
82
  const isEmailLayoutDir = await _emaillayout.isEmailLayoutDir(currDir);
78
83
  if (isEmailLayoutDir) {
79
84
  ctx.resourceDir = buildResourceDirContext("email_layout", currDir);
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get cleanupTempDir () {
13
+ return cleanupTempDir;
14
+ },
15
+ get downloadTemplate () {
16
+ return downloadTemplate;
17
+ },
18
+ get resolveTemplate () {
19
+ return resolveTemplate;
20
+ }
21
+ });
22
+ const _nodeos = /*#__PURE__*/ _interop_require_wildcard(require("node:os"));
23
+ const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
24
+ const _degit = /*#__PURE__*/ _interop_require_default(require("degit"));
25
+ const _fsextra = /*#__PURE__*/ _interop_require_wildcard(require("fs-extra"));
26
+ function _interop_require_default(obj) {
27
+ return obj && obj.__esModule ? obj : {
28
+ default: obj
29
+ };
30
+ }
31
+ function _getRequireWildcardCache(nodeInterop) {
32
+ if (typeof WeakMap !== "function") return null;
33
+ var cacheBabelInterop = new WeakMap();
34
+ var cacheNodeInterop = new WeakMap();
35
+ return (_getRequireWildcardCache = function(nodeInterop) {
36
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
37
+ })(nodeInterop);
38
+ }
39
+ function _interop_require_wildcard(obj, nodeInterop) {
40
+ if (!nodeInterop && obj && obj.__esModule) {
41
+ return obj;
42
+ }
43
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
44
+ return {
45
+ default: obj
46
+ };
47
+ }
48
+ var cache = _getRequireWildcardCache(nodeInterop);
49
+ if (cache && cache.has(obj)) {
50
+ return cache.get(obj);
51
+ }
52
+ var newObj = {
53
+ __proto__: null
54
+ };
55
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
56
+ for(var key in obj){
57
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
58
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
59
+ if (desc && (desc.get || desc.set)) {
60
+ Object.defineProperty(newObj, key, desc);
61
+ } else {
62
+ newObj[key] = obj[key];
63
+ }
64
+ }
65
+ }
66
+ newObj.default = obj;
67
+ if (cache) {
68
+ cache.set(obj, newObj);
69
+ }
70
+ return newObj;
71
+ }
72
+ const DEFAULT_ORG = "knocklabs";
73
+ const DEFAULT_REPO = "templates";
74
+ const TEMP_DIR_PREFIX = "knock-cli-templates";
75
+ function resolveTemplate(templateString) {
76
+ const parts = templateString.split("/");
77
+ let org;
78
+ let repo;
79
+ let subdir;
80
+ if (parts.length === 2) {
81
+ // workflows/some-workflow
82
+ org = DEFAULT_ORG;
83
+ repo = DEFAULT_REPO;
84
+ subdir = parts.join("/");
85
+ }
86
+ if (parts.length === 4) {
87
+ // knocklabs/templates/workflows/some-workflow
88
+ org = parts[0];
89
+ repo = parts[1];
90
+ subdir = parts.slice(2).join("/");
91
+ }
92
+ if (!org || !repo || !subdir) {
93
+ throw new Error(`Invalid template string: ${templateString}. Expected format: org/repo/subdir`);
94
+ }
95
+ return {
96
+ org,
97
+ repo,
98
+ subdir
99
+ };
100
+ }
101
+ function buildSource(org, repo, subdir, branch = "main") {
102
+ const base = `${org}/${repo}`;
103
+ if (subdir) {
104
+ return `${base}/${subdir}#${branch}`;
105
+ }
106
+ return `${base}#${branch}`;
107
+ }
108
+ async function downloadTemplate(templateString) {
109
+ const { org, repo, subdir } = resolveTemplate(templateString);
110
+ const source = buildSource(org, repo, subdir);
111
+ // Create temp directory for template
112
+ const tempDir = _nodepath.default.join(_nodeos.tmpdir(), TEMP_DIR_PREFIX, `${org}-${repo}-${subdir.replace(/\//g, "-")}-${Date.now()}`);
113
+ await _fsextra.ensureDir(tempDir);
114
+ try {
115
+ const emitter = (0, _degit.default)(source, {
116
+ cache: false,
117
+ force: true,
118
+ verbose: false
119
+ });
120
+ await emitter.clone(tempDir);
121
+ return tempDir;
122
+ } catch (error) {
123
+ await _fsextra.remove(tempDir);
124
+ throw new Error(`Failed to download template: ${error instanceof Error ? error.message : "Unknown error"}\n\nSource: ${source}`);
125
+ }
126
+ }
127
+ async function cleanupTempDir(tempDir) {
128
+ if (tempDir && tempDir.includes(TEMP_DIR_PREFIX)) {
129
+ await _fsextra.remove(tempDir);
130
+ }
131
+ }