directus-template-cli 0.7.0-beta.4 → 0.7.0-beta.6

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 (105) hide show
  1. package/bin/dev.js +6 -0
  2. package/bin/run.js +5 -0
  3. package/dist/commands/apply.d.ts +17 -17
  4. package/dist/commands/apply.js +163 -173
  5. package/dist/commands/base.d.ts +15 -0
  6. package/dist/commands/base.js +45 -0
  7. package/dist/commands/extract.d.ts +16 -7
  8. package/dist/commands/extract.js +80 -73
  9. package/dist/commands/init.d.ts +20 -15
  10. package/dist/commands/init.js +189 -126
  11. package/dist/flags/common.d.ts +8 -7
  12. package/dist/flags/common.js +13 -11
  13. package/dist/index.js +1 -5
  14. package/dist/lib/constants.d.ts +3 -5
  15. package/dist/lib/constants.js +8 -13
  16. package/dist/lib/extract/extract-access.js +11 -15
  17. package/dist/lib/extract/extract-assets.js +20 -25
  18. package/dist/lib/extract/extract-collections.js +12 -16
  19. package/dist/lib/extract/extract-content.js +14 -19
  20. package/dist/lib/extract/extract-dashboards.js +22 -28
  21. package/dist/lib/extract/extract-extensions.js +12 -16
  22. package/dist/lib/extract/extract-fields.js +13 -17
  23. package/dist/lib/extract/extract-files.js +15 -19
  24. package/dist/lib/extract/extract-flows.js +22 -28
  25. package/dist/lib/extract/extract-folders.js +15 -19
  26. package/dist/lib/extract/extract-permissions.js +12 -16
  27. package/dist/lib/extract/extract-policies.js +12 -16
  28. package/dist/lib/extract/extract-presets.js +12 -16
  29. package/dist/lib/extract/extract-relations.js +14 -18
  30. package/dist/lib/extract/extract-roles.js +15 -19
  31. package/dist/lib/extract/extract-schema.js +17 -21
  32. package/dist/lib/extract/extract-settings.js +12 -16
  33. package/dist/lib/extract/extract-translations.js +12 -16
  34. package/dist/lib/extract/extract-users.js +15 -19
  35. package/dist/lib/extract/index.js +47 -51
  36. package/dist/lib/init/config.d.ts +1 -1
  37. package/dist/lib/init/config.js +3 -6
  38. package/dist/lib/init/index.d.ts +5 -9
  39. package/dist/lib/init/index.js +105 -85
  40. package/dist/lib/init/types.js +1 -2
  41. package/dist/lib/load/apply-flags.js +17 -23
  42. package/dist/lib/load/index.d.ts +1 -12
  43. package/dist/lib/load/index.js +40 -44
  44. package/dist/lib/load/load-access.js +15 -20
  45. package/dist/lib/load/load-collections.js +27 -32
  46. package/dist/lib/load/load-dashboards.js +19 -25
  47. package/dist/lib/load/load-data.js +43 -49
  48. package/dist/lib/load/load-extensions.js +30 -38
  49. package/dist/lib/load/load-files.js +20 -24
  50. package/dist/lib/load/load-flows.js +23 -29
  51. package/dist/lib/load/load-folders.js +16 -20
  52. package/dist/lib/load/load-permissions.js +13 -17
  53. package/dist/lib/load/load-policies.js +14 -18
  54. package/dist/lib/load/load-presets.js +14 -18
  55. package/dist/lib/load/load-relations.d.ts +2 -0
  56. package/dist/lib/load/load-relations.js +16 -18
  57. package/dist/lib/load/load-roles.js +19 -23
  58. package/dist/lib/load/load-settings.js +18 -21
  59. package/dist/lib/load/load-translations.js +14 -18
  60. package/dist/lib/load/load-users.js +21 -25
  61. package/dist/lib/load/update-required-fields.js +13 -17
  62. package/dist/lib/sdk.d.ts +1 -2
  63. package/dist/lib/sdk.js +27 -27
  64. package/dist/lib/types/extension.js +1 -2
  65. package/dist/lib/types.d.ts +18 -0
  66. package/dist/lib/types.js +1 -0
  67. package/dist/lib/utils/animated-bunny.js +9 -14
  68. package/dist/lib/utils/auth.d.ts +8 -6
  69. package/dist/lib/utils/auth.js +48 -39
  70. package/dist/lib/utils/catch-error.js +8 -11
  71. package/dist/lib/utils/check-template.js +4 -8
  72. package/dist/lib/utils/chunk-array.js +1 -5
  73. package/dist/lib/utils/ensure-dir.js +7 -12
  74. package/dist/lib/utils/filter-fields.js +1 -4
  75. package/dist/lib/utils/get-role-ids.d.ts +1 -1
  76. package/dist/lib/utils/get-role-ids.js +7 -12
  77. package/dist/lib/utils/get-template.js +33 -37
  78. package/dist/lib/utils/logger.js +11 -13
  79. package/dist/lib/utils/open-url.js +5 -8
  80. package/dist/lib/utils/parse-github-url.d.ts +10 -5
  81. package/dist/lib/utils/parse-github-url.js +80 -45
  82. package/dist/lib/utils/path.js +6 -10
  83. package/dist/lib/utils/protected-domains.js +1 -4
  84. package/dist/lib/utils/read-file.js +8 -12
  85. package/dist/lib/utils/read-templates.js +9 -15
  86. package/dist/lib/utils/sanitize-flags.d.ts +3 -0
  87. package/dist/lib/utils/sanitize-flags.js +4 -0
  88. package/dist/lib/utils/system-fields.js +19 -22
  89. package/dist/lib/utils/template-config.d.ts +16 -0
  90. package/dist/lib/utils/template-config.js +34 -0
  91. package/dist/lib/utils/template-defaults.d.ts +1 -1
  92. package/dist/lib/utils/template-defaults.js +5 -14
  93. package/dist/lib/utils/transform-github-url.js +1 -5
  94. package/dist/lib/utils/validate-url.js +3 -6
  95. package/dist/lib/utils/wait.js +1 -5
  96. package/dist/lib/utils/write-to-file.js +8 -11
  97. package/dist/services/docker.js +68 -21
  98. package/dist/services/github.d.ts +1 -1
  99. package/dist/services/github.js +53 -22
  100. package/dist/services/posthog.d.ts +37 -0
  101. package/dist/services/posthog.js +104 -0
  102. package/oclif.manifest.json +32 -13
  103. package/package.json +38 -33
  104. package/bin/dev +0 -17
  105. package/bin/run +0 -5
@@ -1,13 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateReadmeContent = exports.generatePackageJsonContent = void 0;
4
- const tslib_1 = require("tslib");
5
- const slugify_1 = tslib_1.__importDefault(require("slugify"));
6
- const generatePackageJsonContent = (templateName) => {
7
- const slugifiedName = (0, slugify_1.default)(templateName, {
8
- lower: true, // Convert to lowercase
9
- strict: true, // Remove special characters
10
- });
1
+ import slugify from '@sindresorhus/slugify';
2
+ export function generatePackageJsonContent(templateName) {
3
+ const slugifiedName = slugify(templateName);
11
4
  const packageName = `directus-template-${slugifiedName}`;
12
5
  return JSON.stringify({
13
6
  author: '',
@@ -18,9 +11,8 @@ const generatePackageJsonContent = (templateName) => {
18
11
  templateName,
19
12
  version: '1.0.0',
20
13
  }, null, 2);
21
- };
22
- exports.generatePackageJsonContent = generatePackageJsonContent;
23
- const generateReadmeContent = (templateName) => `# ${templateName} Template
14
+ }
15
+ export const generateReadmeContent = (templateName) => `# ${templateName} Template
24
16
 
25
17
  This is a template for [Directus](https://directus.io/) - an open-source headless CMS and API. Use the template-cli to load / apply this template to a blank instance.
26
18
 
@@ -31,4 +23,3 @@ This is a template for [Directus](https://directus.io/) - an open-source headles
31
23
  ## License
32
24
 
33
25
  `;
34
- exports.generateReadmeContent = generateReadmeContent;
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformGitHubUrl = void 0;
4
- function transformGitHubUrl(url) {
1
+ export function transformGitHubUrl(url) {
5
2
  // Regular expression to capture the repository name and any subsequent path after the 'tree'
6
3
  const regex = /github\.com\/([^/]+\/[^/]+)(?:\/tree\/[^/]+\/(.*))?$/;
7
4
  const match = url.match(regex);
@@ -12,4 +9,3 @@ function transformGitHubUrl(url) {
12
9
  }
13
10
  return 'Invalid URL';
14
11
  }
15
- exports.transformGitHubUrl = transformGitHubUrl;
@@ -1,13 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const protected_domains_1 = require("./protected-domains");
4
- function validateUrl(url) {
1
+ import { protectedDomains } from './protected-domains.js';
2
+ export default function validateUrl(url) {
5
3
  try {
6
4
  const parsedUrl = new URL(url);
7
- return !protected_domains_1.protectedDomains.includes(parsedUrl.hostname);
5
+ return !protectedDomains.includes(parsedUrl.hostname);
8
6
  }
9
7
  catch {
10
8
  return false;
11
9
  }
12
10
  }
13
- exports.default = validateUrl;
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.waitFor = void 0;
4
- async function waitFor(checkFn, options = {}) {
1
+ export async function waitFor(checkFn, options = {}) {
5
2
  const { errorMessage = 'Operation timed out', interval = 2000, maxAttempts = 30, } = options;
6
3
  for (let i = 0; i < maxAttempts; i++) {
7
4
  if (await checkFn())
@@ -10,4 +7,3 @@ async function waitFor(checkFn, options = {}) {
10
7
  }
11
8
  throw new Error(errorMessage);
12
9
  }
13
- exports.waitFor = waitFor;
@@ -1,23 +1,20 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
5
- const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
- exports.default = async (fileName, data, dir) => {
1
+ import fs from 'node:fs';
2
+ import path from 'pathe';
3
+ export default async (fileName, data, dir) => {
7
4
  const folders = fileName.split('/');
8
5
  const endFileName = folders.pop();
9
6
  const folderPath = folders.join('/');
10
7
  // Generate the full path where you want to write the file
11
- const fullPath = node_path_1.default.join(dir, folderPath);
8
+ const fullPath = path.join(dir, folderPath);
12
9
  // Check if the directory exists. Create if it doesn't.
13
- if (!node_fs_1.default.existsSync(fullPath)) {
14
- node_fs_1.default.mkdirSync(fullPath, { recursive: true });
10
+ if (!fs.existsSync(fullPath)) {
11
+ fs.mkdirSync(fullPath, { recursive: true });
15
12
  }
16
13
  // Construct the full file path
17
- const fullFilePath = node_path_1.default.join(fullPath, `${endFileName}.json`);
14
+ const fullFilePath = path.join(fullPath, `${endFileName}.json`);
18
15
  try {
19
16
  // Write the file
20
- await node_fs_1.default.promises.writeFile(fullFilePath, JSON.stringify(data, null, 2));
17
+ await fs.promises.writeFile(fullFilePath, JSON.stringify(data, null, 2));
21
18
  // console.log(`Wrote ${fullFilePath}`);
22
19
  }
23
20
  catch (error) {
@@ -1,10 +1,62 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createDocker = void 0;
4
- const tslib_1 = require("tslib");
5
- const prompts_1 = require("@clack/prompts");
6
- const catch_error_1 = tslib_1.__importDefault(require("../lib/utils/catch-error"));
7
- const wait_1 = require("../lib/utils/wait");
1
+ import { spinner } from '@clack/prompts';
2
+ import { execa } from 'execa';
3
+ import net from 'node:net';
4
+ import { ux } from '@oclif/core';
5
+ import catchError from '../lib/utils/catch-error.js';
6
+ import { waitFor } from '../lib/utils/wait.js';
7
+ /**
8
+ * Check if a port is in use and what's using it
9
+ * @param port The port to check
10
+ * @returns Object indicating if port is in use and what's using it
11
+ */
12
+ async function checkPort(port) {
13
+ return new Promise((resolve) => {
14
+ const server = net.createServer();
15
+ server.once('error', async (err) => {
16
+ if (err.code === 'EADDRINUSE') {
17
+ // Try to get information about what's using the port
18
+ try {
19
+ const { stdout } = await execa('lsof', ['-i', `:${port}`]);
20
+ const process = stdout.split('\n')[1]?.split(/\s+/)[0]; // Get process name
21
+ resolve({ inUse: true, process });
22
+ }
23
+ catch {
24
+ resolve({ inUse: true });
25
+ }
26
+ }
27
+ else {
28
+ resolve({ inUse: false });
29
+ }
30
+ });
31
+ server.once('listening', () => {
32
+ server.close();
33
+ resolve({ inUse: false });
34
+ });
35
+ server.listen(port);
36
+ });
37
+ }
38
+ /**
39
+ * Check if required ports are available and warn if they're in use
40
+ * @returns Promise<void>
41
+ */
42
+ async function checkRequiredPorts() {
43
+ const portsToCheck = [
44
+ { port: 8055, name: 'Directus API' },
45
+ { port: 5432, name: 'PostgreSQL' },
46
+ ];
47
+ let hasConflicts = false;
48
+ for (const { port, name } of portsToCheck) {
49
+ const status = await checkPort(port);
50
+ if (status.inUse) {
51
+ hasConflicts = true;
52
+ const process = status.process ? ` by ${status.process}` : '';
53
+ ux.warn(`Port ${port} (${name}) is already in use${process}`);
54
+ }
55
+ }
56
+ if (hasConflicts) {
57
+ ux.warn('Please stop any conflicting services before continuing.');
58
+ }
59
+ }
8
60
  /**
9
61
  * Check if Docker is installed and running
10
62
  * @returns {Promise<DockerCheckResult>} Docker installation and running status
@@ -12,7 +64,6 @@ const wait_1 = require("../lib/utils/wait");
12
64
  async function checkDocker() {
13
65
  try {
14
66
  // Check if Docker is installed
15
- const { execa } = await Promise.resolve().then(() => tslib_1.__importStar(require('execa')));
16
67
  const versionResult = await execa('docker', ['--version']);
17
68
  const isInstalled = versionResult.exitCode === 0;
18
69
  if (!isInstalled) {
@@ -43,19 +94,18 @@ async function checkDocker() {
43
94
  */
44
95
  async function startContainers(cwd) {
45
96
  try {
46
- const { execa } = await Promise.resolve().then(() => tslib_1.__importStar(require('execa')));
47
- // ux.action.start('Starting Docker containers')
48
- const s = (0, prompts_1.spinner)();
97
+ // Check if required ports are available
98
+ await checkRequiredPorts();
99
+ const s = spinner();
49
100
  s.start('Starting Docker containers');
50
101
  return execa('docker-compose', ['up', '-d'], {
51
102
  cwd,
52
- // stdio: 'inherit',
53
103
  }).then(() => {
54
104
  s.stop('Docker containers running!');
55
105
  });
56
106
  }
57
107
  catch (error) {
58
- (0, catch_error_1.default)(error, {
108
+ catchError(error, {
59
109
  context: { cwd, function: 'startContainers' },
60
110
  fatal: true,
61
111
  logToFile: true,
@@ -70,14 +120,12 @@ async function startContainers(cwd) {
70
120
  */
71
121
  async function stopContainers(cwd) {
72
122
  try {
73
- const { execa } = await Promise.resolve().then(() => tslib_1.__importStar(require('execa')));
74
123
  return execa('docker-compose', ['down'], {
75
124
  cwd,
76
- // stdio: 'inherit',
77
125
  }).then(() => { });
78
126
  }
79
127
  catch (error) {
80
- (0, catch_error_1.default)(error, {
128
+ catchError(error, {
81
129
  context: { cwd, function: 'stopContainers' },
82
130
  fatal: false,
83
131
  logToFile: true,
@@ -92,10 +140,10 @@ async function stopContainers(cwd) {
92
140
  */
93
141
  function createWaitForHealthy(config) {
94
142
  async function waitForHealthy(healthCheckUrl) {
95
- const s = (0, prompts_1.spinner)();
143
+ const s = spinner();
96
144
  s.start('Waiting for Directus to be ready.');
97
145
  try {
98
- await (0, wait_1.waitFor)(async () => {
146
+ await waitFor(async () => {
99
147
  try {
100
148
  const response = await fetch(healthCheckUrl);
101
149
  return response.ok;
@@ -113,7 +161,7 @@ function createWaitForHealthy(config) {
113
161
  }
114
162
  catch (error) {
115
163
  s.stop('');
116
- (0, catch_error_1.default)(error, {
164
+ catchError(error, {
117
165
  context: { function: 'waitForHealthy', url: healthCheckUrl },
118
166
  fatal: true,
119
167
  logToFile: true,
@@ -128,7 +176,7 @@ function createWaitForHealthy(config) {
128
176
  * @param {DockerConfig} config - The Docker configuration
129
177
  * @returns {DockerService} - Returns a Docker service instance
130
178
  */
131
- function createDocker(config) {
179
+ export function createDocker(config) {
132
180
  return {
133
181
  checkDocker,
134
182
  startContainers,
@@ -136,4 +184,3 @@ function createDocker(config) {
136
184
  waitForHealthy: createWaitForHealthy(config),
137
185
  };
138
186
  }
139
- exports.createDocker = createDocker;
@@ -1,4 +1,4 @@
1
- import { parseGitHubUrl } from '../lib/utils/parse-github-url';
1
+ import { parseGitHubUrl } from '../lib/utils/parse-github-url.js';
2
2
  interface GitHubUrlParts {
3
3
  owner: string;
4
4
  path?: string;
@@ -1,11 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createGitHub = void 0;
4
- const rest_1 = require("@octokit/rest");
5
- const constants_1 = require("../lib/constants");
6
- const parse_github_url_1 = require("../lib/utils/parse-github-url");
7
- function createGitHub(token) {
8
- const octokit = new rest_1.Octokit({
1
+ import { Octokit } from '@octokit/rest';
2
+ import { DEFAULT_REPO } from '../lib/constants.js';
3
+ import { parseGitHubUrl } from '../lib/utils/parse-github-url.js';
4
+ export function createGitHub(token) {
5
+ const octokit = new Octokit({
9
6
  auth: token,
10
7
  });
11
8
  /**
@@ -15,19 +12,50 @@ function createGitHub(token) {
15
12
  * @returns The directories for the template.
16
13
  */
17
14
  async function getTemplateDirectories(template, customUrl) {
18
- const repo = customUrl ? (0, parse_github_url_1.parseGitHubUrl)(customUrl) : constants_1.DEFAULT_REPO;
15
+ // If template is a URL, parse it directly
16
+ if (template.startsWith('http')) {
17
+ const repo = parseGitHubUrl(template);
18
+ try {
19
+ const { data } = await octokit.rest.repos.getContent({
20
+ owner: repo.owner,
21
+ repo: repo.repo,
22
+ path: repo.path || '',
23
+ ref: repo.ref,
24
+ });
25
+ if (!Array.isArray(data))
26
+ return [];
27
+ // For direct URLs, we don't filter out the directus directory
28
+ // as the entire repo might be a directus template
29
+ return data
30
+ .filter(item => item.type === 'dir')
31
+ .map(item => item.name);
32
+ }
33
+ catch (error) {
34
+ // If we can't get contents, return empty array
35
+ // This indicates no frontends are available
36
+ return [];
37
+ }
38
+ }
39
+ // Otherwise use default repo behavior
40
+ const repo = customUrl ? parseGitHubUrl(customUrl) : DEFAULT_REPO;
19
41
  const templatePath = repo.path ? `${repo.path}/${template}` : template;
20
- const { data } = await octokit.rest.repos.getContent({
21
- owner: repo.owner,
22
- path: templatePath,
23
- ref: repo.ref,
24
- repo: repo.repo,
25
- });
26
- if (!Array.isArray(data))
42
+ try {
43
+ const { data } = await octokit.rest.repos.getContent({
44
+ owner: repo.owner,
45
+ path: templatePath,
46
+ ref: repo.ref,
47
+ repo: repo.repo,
48
+ });
49
+ if (!Array.isArray(data))
50
+ return [];
51
+ return data
52
+ .filter(item => item.type === 'dir' && item.name !== 'directus')
53
+ .map(item => item.name);
54
+ }
55
+ catch (error) {
56
+ // If we can't get contents, return empty array
27
57
  return [];
28
- return data
29
- .filter(item => item.type === 'dir' && item.name !== 'directus')
30
- .map(item => item.name);
58
+ }
31
59
  }
32
60
  /**
33
61
  * Get the templates for a repository.
@@ -35,7 +63,11 @@ function createGitHub(token) {
35
63
  * @returns The templates for the repository.
36
64
  */
37
65
  async function getTemplates(customUrl) {
38
- const repo = customUrl ? (0, parse_github_url_1.parseGitHubUrl)(customUrl) : constants_1.DEFAULT_REPO;
66
+ // If customUrl is provided and it's a full repository URL, return it as the only template
67
+ if (customUrl?.startsWith('http')) {
68
+ return [customUrl];
69
+ }
70
+ const repo = customUrl ? parseGitHubUrl(customUrl) : DEFAULT_REPO;
39
71
  const { data } = await octokit.rest.repos.getContent({
40
72
  owner: repo.owner,
41
73
  path: repo.path || '',
@@ -51,7 +83,6 @@ function createGitHub(token) {
51
83
  return {
52
84
  getTemplateDirectories,
53
85
  getTemplates,
54
- parseGitHubUrl: parse_github_url_1.parseGitHubUrl,
86
+ parseGitHubUrl,
55
87
  };
56
88
  }
57
- exports.createGitHub = createGitHub;
@@ -0,0 +1,37 @@
1
+ import type { Config } from '@oclif/core';
2
+ import { PostHog } from 'posthog-node';
3
+ /**
4
+ * Initialize and get the PostHog client
5
+ * @param debug Whether to log debug information
6
+ * @returns The PostHog client
7
+ */
8
+ export declare function getClient(debug?: boolean): PostHog;
9
+ /**
10
+ * Shutdown the PostHog client
11
+ * @param debug Whether to log debug information
12
+ * @returns void
13
+ */
14
+ export declare function shutdown(debug?: boolean): Promise<void>;
15
+ /**
16
+ * Track an event in PostHog
17
+ * @param options The tracking options
18
+ * @param options.lifecycle The lifecycle event to track ('start', 'complete', 'error')
19
+ * @param options.distinctId The distinct ID for the user
20
+ * @param options.command Optional command name (for command tracking)
21
+ * @param options.flags Optional command flags
22
+ * @param options.runId Optional run ID
23
+ * @param options.config Optional config object
24
+ * @param options.properties Optional additional properties to track
25
+ * @param options.debug Whether to log debug information
26
+ */
27
+ export declare function track({ lifecycle, distinctId, command, flags, runId, message, config, properties, debug }: {
28
+ lifecycle: 'start' | 'complete' | 'error';
29
+ message?: string;
30
+ distinctId: string;
31
+ command?: string;
32
+ flags?: Record<string, unknown>;
33
+ runId?: string;
34
+ config?: Config;
35
+ properties?: Record<string, unknown>;
36
+ debug?: boolean;
37
+ }): void;
@@ -0,0 +1,104 @@
1
+ import { ux } from '@oclif/core';
2
+ import { PostHog } from 'posthog-node';
3
+ import { POSTHOG_PUBLIC_KEY, POSTHOG_HOST } from '../lib/constants.js';
4
+ import { sanitizeFlags } from '../lib/utils/sanitize-flags.js';
5
+ // Create a singleton client using module scope
6
+ let client = null;
7
+ /**
8
+ * Initialize and get the PostHog client
9
+ * @param debug Whether to log debug information
10
+ * @returns The PostHog client
11
+ */
12
+ export function getClient(debug = false) {
13
+ if (debug)
14
+ ux.stdout('Initializing PostHog client...');
15
+ if (!client) {
16
+ client = new PostHog(POSTHOG_PUBLIC_KEY, {
17
+ host: POSTHOG_HOST,
18
+ disableGeoip: false,
19
+ });
20
+ // Add error handling
21
+ client.on('error', err => {
22
+ ux.warn(`PostHog Error: ${err}`);
23
+ });
24
+ }
25
+ if (debug)
26
+ ux.stdout('PostHog client initialized successfully');
27
+ return client;
28
+ }
29
+ /**
30
+ * Shutdown the PostHog client
31
+ * @param debug Whether to log debug information
32
+ * @returns void
33
+ */
34
+ export async function shutdown(debug = false) {
35
+ if (debug)
36
+ ux.stdout('Shutting down PostHog client...');
37
+ if (!client)
38
+ return;
39
+ try {
40
+ await client.shutdown();
41
+ client = null;
42
+ if (debug)
43
+ ux.stdout('PostHog client shut down successfully');
44
+ }
45
+ catch (error) {
46
+ ux.warn(`Error shutting down PostHog client: ${error}`);
47
+ }
48
+ }
49
+ /**
50
+ * Track an event in PostHog
51
+ * @param options The tracking options
52
+ * @param options.lifecycle The lifecycle event to track ('start', 'complete', 'error')
53
+ * @param options.distinctId The distinct ID for the user
54
+ * @param options.command Optional command name (for command tracking)
55
+ * @param options.flags Optional command flags
56
+ * @param options.runId Optional run ID
57
+ * @param options.config Optional config object
58
+ * @param options.properties Optional additional properties to track
59
+ * @param options.debug Whether to log debug information
60
+ */
61
+ export function track({ lifecycle, distinctId, command, flags, runId, message, config, properties = {}, debug = false }) {
62
+ if (debug)
63
+ ux.stdout('Tracking event...');
64
+ const phClient = getClient(debug);
65
+ const eventProperties = command
66
+ ? {
67
+ runId,
68
+ message,
69
+ ...properties,
70
+ ...getEnvironmentInfo(config),
71
+ // Always sanitize sensitive flags
72
+ ...sanitizeFlags(flags),
73
+ }
74
+ : properties;
75
+ if (debug) {
76
+ ux.stdout('Capturing event...');
77
+ ux.stdout(JSON.stringify(eventProperties));
78
+ }
79
+ phClient.capture({
80
+ distinctId,
81
+ event: `directus_template_cli.${command}.${lifecycle}`,
82
+ properties: eventProperties
83
+ });
84
+ if (debug)
85
+ ux.stdout('Event tracked successfully');
86
+ }
87
+ /**
88
+ * Get environment info
89
+ * @param config The config to get environment info from
90
+ * @returns The environment info
91
+ */
92
+ function getEnvironmentInfo(config) {
93
+ return {
94
+ // PostHog properties
95
+ $os: process.platform,
96
+ $raw_user_agent: config?.userAgent || 'unknown',
97
+ // Custom properties
98
+ arch: process.arch || 'unknown',
99
+ nodeVersion: process.version,
100
+ platform: config?.platform || 'unknown',
101
+ shell: config?.shell || 'unknown',
102
+ version: config?.version || 'unknown',
103
+ };
104
+ }
@@ -169,13 +169,32 @@
169
169
  "pluginType": "core",
170
170
  "strict": true,
171
171
  "enableJsonFlag": false,
172
- "isESM": false,
172
+ "isESM": true,
173
173
  "relativePath": [
174
174
  "dist",
175
175
  "commands",
176
176
  "apply.js"
177
177
  ]
178
178
  },
179
+ "base": {
180
+ "aliases": [],
181
+ "args": {},
182
+ "flags": {},
183
+ "hasDynamicHelp": false,
184
+ "hiddenAliases": [],
185
+ "id": "base",
186
+ "pluginAlias": "directus-template-cli",
187
+ "pluginName": "directus-template-cli",
188
+ "pluginType": "core",
189
+ "strict": true,
190
+ "enableJsonFlag": false,
191
+ "isESM": true,
192
+ "relativePath": [
193
+ "dist",
194
+ "commands",
195
+ "base.js"
196
+ ]
197
+ },
179
198
  "extract": {
180
199
  "aliases": [],
181
200
  "args": {},
@@ -272,7 +291,7 @@
272
291
  "pluginType": "core",
273
292
  "strict": true,
274
293
  "enableJsonFlag": false,
275
- "isESM": false,
294
+ "isESM": true,
276
295
  "relativePath": [
277
296
  "dist",
278
297
  "commands",
@@ -293,8 +312,8 @@
293
312
  "examples": [
294
313
  "$ directus-template-cli init",
295
314
  "$ directus-template-cli init my-project",
296
- "$ directus-template-cli init --frontend=nextjs --template=simple-cms --programmatic",
297
- "$ directus-template-cli init my-project --frontend=nextjs --template=simple-cms --programmatic"
315
+ "$ directus-template-cli init --frontend=nextjs --template=simple-cms",
316
+ "$ directus-template-cli init my-project --frontend=nextjs --template=simple-cms"
298
317
  ],
299
318
  "flags": {
300
319
  "frontend": {
@@ -328,19 +347,19 @@
328
347
  "allowNo": false,
329
348
  "type": "boolean"
330
349
  },
331
- "programmatic": {
332
- "char": "p",
333
- "description": "Run in programmatic mode (non-interactive)",
334
- "name": "programmatic",
335
- "allowNo": false,
336
- "type": "boolean"
337
- },
338
350
  "template": {
339
351
  "description": "Template name (e.g., simple-cms) or GitHub URL (e.g., https://github.com/directus-labs/starters/tree/main/simple-cms)",
340
352
  "name": "template",
341
353
  "hasDynamicHelp": false,
342
354
  "multiple": false,
343
355
  "type": "option"
356
+ },
357
+ "disableTelemetry": {
358
+ "description": "Disable telemetry",
359
+ "env": "DISABLE_TELEMETRY",
360
+ "name": "disableTelemetry",
361
+ "allowNo": false,
362
+ "type": "boolean"
344
363
  }
345
364
  },
346
365
  "hasDynamicHelp": false,
@@ -351,7 +370,7 @@
351
370
  "pluginType": "core",
352
371
  "strict": true,
353
372
  "enableJsonFlag": false,
354
- "isESM": false,
373
+ "isESM": true,
355
374
  "relativePath": [
356
375
  "dist",
357
376
  "commands",
@@ -359,5 +378,5 @@
359
378
  ]
360
379
  }
361
380
  },
362
- "version": "0.7.0-beta.4"
381
+ "version": "0.7.0-beta.6"
363
382
  }