@angeloashmore/prismic-cli-poc 0.0.0-canary.2ff9563

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 (119) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +98 -0
  3. package/dist/index.mjs +1996 -0
  4. package/package.json +52 -0
  5. package/src/custom-type-add-field-boolean.ts +171 -0
  6. package/src/custom-type-add-field-color.ts +158 -0
  7. package/src/custom-type-add-field-date.ts +161 -0
  8. package/src/custom-type-add-field-embed.ts +158 -0
  9. package/src/custom-type-add-field-geo-point.ts +155 -0
  10. package/src/custom-type-add-field-image.ts +158 -0
  11. package/src/custom-type-add-field-key-text.ts +158 -0
  12. package/src/custom-type-add-field-link.ts +180 -0
  13. package/src/custom-type-add-field-number.ts +190 -0
  14. package/src/custom-type-add-field-rich-text.ts +181 -0
  15. package/src/custom-type-add-field-select.ts +164 -0
  16. package/src/custom-type-add-field-timestamp.ts +161 -0
  17. package/src/custom-type-add-field-uid.ts +158 -0
  18. package/src/custom-type-add-field.ts +111 -0
  19. package/src/custom-type-connect-slice.ts +221 -0
  20. package/src/custom-type-create.ts +92 -0
  21. package/src/custom-type-disconnect-slice.ts +179 -0
  22. package/src/custom-type-list.ts +110 -0
  23. package/src/custom-type-remove-field.ts +161 -0
  24. package/src/custom-type-remove.ts +126 -0
  25. package/src/custom-type-set-name.ts +128 -0
  26. package/src/custom-type-view.ts +118 -0
  27. package/src/custom-type.ts +85 -0
  28. package/src/index.ts +100 -0
  29. package/src/init.ts +62 -0
  30. package/src/lib/auth.ts +60 -0
  31. package/src/lib/config.ts +111 -0
  32. package/src/lib/file.ts +49 -0
  33. package/src/lib/json.ts +3 -0
  34. package/src/lib/request.ts +116 -0
  35. package/src/lib/slice.ts +112 -0
  36. package/src/lib/url.ts +25 -0
  37. package/src/locale-add.ts +116 -0
  38. package/src/locale-list.ts +107 -0
  39. package/src/locale-remove.ts +88 -0
  40. package/src/locale-set-default.ts +131 -0
  41. package/src/locale.ts +60 -0
  42. package/src/login.ts +143 -0
  43. package/src/logout.ts +36 -0
  44. package/src/page-type-add-field-boolean.ts +171 -0
  45. package/src/page-type-add-field-color.ts +158 -0
  46. package/src/page-type-add-field-date.ts +161 -0
  47. package/src/page-type-add-field-embed.ts +158 -0
  48. package/src/page-type-add-field-geo-point.ts +155 -0
  49. package/src/page-type-add-field-image.ts +158 -0
  50. package/src/page-type-add-field-key-text.ts +158 -0
  51. package/src/page-type-add-field-link.ts +180 -0
  52. package/src/page-type-add-field-number.ts +190 -0
  53. package/src/page-type-add-field-rich-text.ts +181 -0
  54. package/src/page-type-add-field-select.ts +164 -0
  55. package/src/page-type-add-field-timestamp.ts +161 -0
  56. package/src/page-type-add-field-uid.ts +158 -0
  57. package/src/page-type-add-field.ts +111 -0
  58. package/src/page-type-connect-slice.ts +221 -0
  59. package/src/page-type-create.ts +93 -0
  60. package/src/page-type-disconnect-slice.ts +179 -0
  61. package/src/page-type-list.ts +109 -0
  62. package/src/page-type-remove-field.ts +161 -0
  63. package/src/page-type-remove.ts +126 -0
  64. package/src/page-type-set-name.ts +128 -0
  65. package/src/page-type-set-repeatable.ts +137 -0
  66. package/src/page-type-view.ts +118 -0
  67. package/src/page-type.ts +90 -0
  68. package/src/preview-add.ts +126 -0
  69. package/src/preview-list.ts +106 -0
  70. package/src/preview-remove.ts +109 -0
  71. package/src/preview-set-name.ts +137 -0
  72. package/src/preview.ts +60 -0
  73. package/src/repo-create.ts +136 -0
  74. package/src/repo-list.ts +100 -0
  75. package/src/repo-set-name.ts +102 -0
  76. package/src/repo-view.ts +113 -0
  77. package/src/repo.ts +60 -0
  78. package/src/slice-add-field-boolean.ts +150 -0
  79. package/src/slice-add-field-color.ts +137 -0
  80. package/src/slice-add-field-date.ts +137 -0
  81. package/src/slice-add-field-embed.ts +137 -0
  82. package/src/slice-add-field-geo-point.ts +134 -0
  83. package/src/slice-add-field-image.ts +134 -0
  84. package/src/slice-add-field-key-text.ts +137 -0
  85. package/src/slice-add-field-link.ts +155 -0
  86. package/src/slice-add-field-number.ts +137 -0
  87. package/src/slice-add-field-rich-text.ts +160 -0
  88. package/src/slice-add-field-select.ts +143 -0
  89. package/src/slice-add-field-timestamp.ts +137 -0
  90. package/src/slice-add-field.ts +106 -0
  91. package/src/slice-add-variation.ts +137 -0
  92. package/src/slice-create.ts +129 -0
  93. package/src/slice-list-variations.ts +67 -0
  94. package/src/slice-list.ts +88 -0
  95. package/src/slice-remove-field.ts +117 -0
  96. package/src/slice-remove-variation.ts +108 -0
  97. package/src/slice-remove.ts +81 -0
  98. package/src/slice-rename.ts +112 -0
  99. package/src/slice-view.ts +77 -0
  100. package/src/slice.ts +90 -0
  101. package/src/sync.ts +309 -0
  102. package/src/token-create.ts +185 -0
  103. package/src/token-delete.ts +161 -0
  104. package/src/token-list.ts +212 -0
  105. package/src/token-set-name.ts +165 -0
  106. package/src/token.ts +60 -0
  107. package/src/webhook-add-header.ts +118 -0
  108. package/src/webhook-create.ts +152 -0
  109. package/src/webhook-disable.ts +109 -0
  110. package/src/webhook-enable.ts +132 -0
  111. package/src/webhook-list.ts +93 -0
  112. package/src/webhook-remove-header.ts +117 -0
  113. package/src/webhook-remove.ts +106 -0
  114. package/src/webhook-set-triggers.ts +148 -0
  115. package/src/webhook-status.ts +90 -0
  116. package/src/webhook-test.ts +106 -0
  117. package/src/webhook-view.ts +147 -0
  118. package/src/webhook.ts +95 -0
  119. package/src/whoami.ts +62 -0
@@ -0,0 +1,113 @@
1
+ import { exec } from "node:child_process";
2
+ import { parseArgs } from "node:util";
3
+ import * as v from "valibot";
4
+
5
+ import { isAuthenticated } from "./lib/auth";
6
+ import { safeGetRepositoryFromConfig } from "./lib/config";
7
+ import { stringify } from "./lib/json";
8
+ import { ForbiddenRequestError, request } from "./lib/request";
9
+ import { getRepoUrl, getUserServiceUrl } from "./lib/url";
10
+
11
+ const HELP = `
12
+ View a Prismic repository.
13
+
14
+ By default, this command reads the repository from prismic.config.json at the
15
+ project root.
16
+
17
+ USAGE
18
+ prismic repo view [flags]
19
+
20
+ FLAGS
21
+ -w, --web Open repository in browser
22
+ -r, --repo string Repository domain
23
+ -h, --help Show help for command
24
+
25
+ LEARN MORE
26
+ Use \`prismic <command> <subcommand> --help\` for more information about a command.
27
+ `.trim();
28
+
29
+ const ProfileSchema = v.object({
30
+ repositories: v.array(
31
+ v.object({
32
+ domain: v.string(),
33
+ name: v.optional(v.string()),
34
+ }),
35
+ ),
36
+ });
37
+
38
+ export async function repoView(): Promise<void> {
39
+ const {
40
+ values: { help, repo = await safeGetRepositoryFromConfig(), web },
41
+ } = parseArgs({
42
+ args: process.argv.slice(4), // skip: node, script, "repo", "view"
43
+ options: {
44
+ web: { type: "boolean", short: "w" },
45
+ repo: { type: "string", short: "r" },
46
+ help: { type: "boolean", short: "h" },
47
+ },
48
+ });
49
+
50
+ if (help) {
51
+ console.info(HELP);
52
+ return;
53
+ }
54
+
55
+ if (!repo) {
56
+ console.error("Missing prismic.config.json or --repo option");
57
+ process.exitCode = 1;
58
+ return;
59
+ }
60
+
61
+ const repoUrl = await getRepoUrl(repo);
62
+
63
+ if (web) {
64
+ openInBrowser(repoUrl.toString());
65
+ console.info(`Opening ${repoUrl}`);
66
+ return;
67
+ }
68
+
69
+ const authenticated = await isAuthenticated();
70
+ if (!authenticated) {
71
+ handleUnauthenticated();
72
+ return;
73
+ }
74
+
75
+ const response = await fetchProfile();
76
+ if (!response.ok) {
77
+ if (response.error instanceof ForbiddenRequestError) {
78
+ handleUnauthenticated();
79
+ } else {
80
+ console.error(`Failed to fetch repository info: ${stringify(response.value)}`);
81
+ process.exitCode = 1;
82
+ }
83
+ return;
84
+ }
85
+
86
+ const repoData = response.value.repositories.find((r) => r.domain === repo);
87
+ if (!repoData) {
88
+ console.error(`Repository not found: ${repo}`);
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+
93
+ const name = repoData.name || "(no name)";
94
+ console.info(`Name: ${name}`);
95
+ console.info(`URL: ${repoUrl}`);
96
+ }
97
+
98
+ async function fetchProfile(): ReturnType<typeof request<v.InferOutput<typeof ProfileSchema>>> {
99
+ const url = new URL("profile", await getUserServiceUrl());
100
+ return await request(url, { schema: ProfileSchema });
101
+ }
102
+
103
+ function openInBrowser(url: string): void {
104
+ const cmd =
105
+ process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
106
+
107
+ exec(`${cmd} "${url}"`);
108
+ }
109
+
110
+ function handleUnauthenticated() {
111
+ console.error("Not logged in. Run `prismic login` first.");
112
+ process.exitCode = 1;
113
+ }
package/src/repo.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ import { repoCreate } from "./repo-create";
4
+ import { repoList } from "./repo-list";
5
+ import { repoSetName } from "./repo-set-name";
6
+ import { repoView } from "./repo-view";
7
+
8
+ const HELP = `
9
+ Manage Prismic repositories.
10
+
11
+ USAGE
12
+ prismic repo <command> [flags]
13
+
14
+ COMMANDS
15
+ create Create a new Prismic repository
16
+ list List all repositories
17
+ view View repository details
18
+ set-name Set repository display name
19
+
20
+ FLAGS
21
+ -h, --help Show help for command
22
+
23
+ LEARN MORE
24
+ Use \`prismic repo <command> --help\` for more information about a command.
25
+ `.trim();
26
+
27
+ export async function repo(): Promise<void> {
28
+ const {
29
+ positionals: [subcommand],
30
+ } = parseArgs({
31
+ args: process.argv.slice(3), // skip: node, script, "repo"
32
+ options: {
33
+ help: { type: "boolean", short: "h" },
34
+ },
35
+ allowPositionals: true,
36
+ strict: false,
37
+ });
38
+
39
+ switch (subcommand) {
40
+ case "create":
41
+ await repoCreate();
42
+ break;
43
+ case "list":
44
+ await repoList();
45
+ break;
46
+ case "view":
47
+ await repoView();
48
+ break;
49
+ case "set-name":
50
+ await repoSetName();
51
+ break;
52
+ default: {
53
+ if (subcommand) {
54
+ console.error(`Unknown repo subcommand: ${subcommand}\n`);
55
+ process.exitCode = 1;
56
+ }
57
+ console.info(HELP);
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,150 @@
1
+ import type { BooleanField, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { writeFile } from "node:fs/promises";
4
+ import { parseArgs } from "node:util";
5
+
6
+ import { stringify } from "./lib/json";
7
+ import { findSliceModel } from "./lib/slice";
8
+
9
+ const HELP = `
10
+ Add a boolean (toggle) field to an existing slice.
11
+
12
+ USAGE
13
+ prismic slice add-field boolean <slice-id> <field-id> [flags]
14
+
15
+ ARGUMENTS
16
+ slice-id Slice identifier (required)
17
+ field-id Field identifier (required)
18
+
19
+ FLAGS
20
+ -v, --variation string Target variation (default: first variation)
21
+ -l, --label string Display label for the field
22
+ --default Set default value to true
23
+ --true-label string Label shown when toggle is on
24
+ --false-label string Label shown when toggle is off
25
+ -h, --help Show help for command
26
+
27
+ EXAMPLES
28
+ prismic slice add-field boolean my_slice featured
29
+ prismic slice add-field boolean hero show_overlay --default
30
+ prismic slice add-field boolean product available --true-label "In Stock" --false-label "Out of Stock"
31
+ `.trim();
32
+
33
+ export async function sliceAddFieldBoolean(): Promise<void> {
34
+ const {
35
+ values: {
36
+ help,
37
+ variation,
38
+ label,
39
+ default: defaultValue,
40
+ "true-label": trueLabel,
41
+ "false-label": falseLabel,
42
+ },
43
+ positionals: [sliceId, fieldId],
44
+ } = parseArgs({
45
+ args: process.argv.slice(5), // skip: node, script, "slice", "add-field", "boolean"
46
+ options: {
47
+ variation: { type: "string", short: "v" },
48
+ label: { type: "string", short: "l" },
49
+ default: { type: "boolean" },
50
+ "true-label": { type: "string" },
51
+ "false-label": { type: "string" },
52
+ help: { type: "boolean", short: "h" },
53
+ },
54
+ allowPositionals: true,
55
+ });
56
+
57
+ if (help) {
58
+ console.info(HELP);
59
+ return;
60
+ }
61
+
62
+ if (!sliceId) {
63
+ console.error("Missing required argument: slice-id\n");
64
+ console.error("Usage: prismic slice add-field boolean <slice-id> <field-id>");
65
+ process.exitCode = 1;
66
+ return;
67
+ }
68
+
69
+ if (!fieldId) {
70
+ console.error("Missing required argument: field-id\n");
71
+ console.error("Usage: prismic slice add-field boolean <slice-id> <field-id>");
72
+ process.exitCode = 1;
73
+ return;
74
+ }
75
+
76
+ // Find the slice model
77
+ const result = await findSliceModel(sliceId);
78
+ if (!result.ok) {
79
+ console.error(result.error);
80
+ process.exitCode = 1;
81
+ return;
82
+ }
83
+
84
+ const { model, modelPath } = result;
85
+
86
+ // Check for variations
87
+ if (model.variations.length === 0) {
88
+ console.error(`Slice "${sliceId}" has no variations.\n`);
89
+ console.error("Add a variation first before adding fields.");
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+
94
+ // Find target variation
95
+ const targetVariation = variation
96
+ ? model.variations.find((v) => v.id === variation)
97
+ : model.variations[0];
98
+
99
+ if (!targetVariation) {
100
+ console.error(`Variation "${variation}" not found in slice "${sliceId}"\n`);
101
+ console.error(`Available variations: ${model.variations.map((v) => v.id).join(", ")}`);
102
+ process.exitCode = 1;
103
+ return;
104
+ }
105
+
106
+ // Initialize primary if it doesn't exist
107
+ if (!targetVariation.primary) {
108
+ targetVariation.primary = {};
109
+ }
110
+
111
+ // Check if field already exists in any variation
112
+ for (const v of model.variations) {
113
+ if (v.primary?.[fieldId]) {
114
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
115
+ process.exitCode = 1;
116
+ return;
117
+ }
118
+ }
119
+
120
+ // Build field definition
121
+ const fieldDefinition: BooleanField = {
122
+ type: "Boolean",
123
+ config: {
124
+ ...(label && { label }),
125
+ ...(defaultValue && { default_value: true }),
126
+ ...(trueLabel && { placeholder_true: trueLabel }),
127
+ ...(falseLabel && { placeholder_false: falseLabel }),
128
+ },
129
+ };
130
+
131
+ // Add field to variation
132
+ targetVariation.primary[fieldId] = fieldDefinition;
133
+
134
+ // Write updated model
135
+ try {
136
+ await writeFile(modelPath, stringify(model as SharedSlice));
137
+ } catch (error) {
138
+ if (error instanceof Error) {
139
+ console.error(`Failed to update slice: ${error.message}`);
140
+ } else {
141
+ console.error("Failed to update slice");
142
+ }
143
+ process.exitCode = 1;
144
+ return;
145
+ }
146
+
147
+ console.info(
148
+ `Added field "${fieldId}" (Boolean) to "${targetVariation.id}" variation in ${sliceId}`,
149
+ );
150
+ }
@@ -0,0 +1,137 @@
1
+ import type { Color, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { writeFile } from "node:fs/promises";
4
+ import { parseArgs } from "node:util";
5
+
6
+ import { stringify } from "./lib/json";
7
+ import { findSliceModel } from "./lib/slice";
8
+
9
+ const HELP = `
10
+ Add a color picker field to an existing slice.
11
+
12
+ USAGE
13
+ prismic slice add-field color <slice-id> <field-id> [flags]
14
+
15
+ ARGUMENTS
16
+ slice-id Slice identifier (required)
17
+ field-id Field identifier (required)
18
+
19
+ FLAGS
20
+ -v, --variation string Target variation (default: first variation)
21
+ -l, --label string Display label for the field
22
+ -p, --placeholder string Placeholder text
23
+ -h, --help Show help for command
24
+
25
+ EXAMPLES
26
+ prismic slice add-field color my_slice background_color
27
+ prismic slice add-field color hero accent --label "Accent Color"
28
+ prismic slice add-field color banner theme_color --variation "dark"
29
+ `.trim();
30
+
31
+ export async function sliceAddFieldColor(): Promise<void> {
32
+ const {
33
+ values: { help, variation, label, placeholder },
34
+ positionals: [sliceId, fieldId],
35
+ } = parseArgs({
36
+ args: process.argv.slice(5), // skip: node, script, "slice", "add-field", "color"
37
+ options: {
38
+ variation: { type: "string", short: "v" },
39
+ label: { type: "string", short: "l" },
40
+ placeholder: { type: "string", short: "p" },
41
+ help: { type: "boolean", short: "h" },
42
+ },
43
+ allowPositionals: true,
44
+ });
45
+
46
+ if (help) {
47
+ console.info(HELP);
48
+ return;
49
+ }
50
+
51
+ if (!sliceId) {
52
+ console.error("Missing required argument: slice-id\n");
53
+ console.error("Usage: prismic slice add-field color <slice-id> <field-id>");
54
+ process.exitCode = 1;
55
+ return;
56
+ }
57
+
58
+ if (!fieldId) {
59
+ console.error("Missing required argument: field-id\n");
60
+ console.error("Usage: prismic slice add-field color <slice-id> <field-id>");
61
+ process.exitCode = 1;
62
+ return;
63
+ }
64
+
65
+ // Find the slice model
66
+ const result = await findSliceModel(sliceId);
67
+ if (!result.ok) {
68
+ console.error(result.error);
69
+ process.exitCode = 1;
70
+ return;
71
+ }
72
+
73
+ const { model, modelPath } = result;
74
+
75
+ // Check for variations
76
+ if (model.variations.length === 0) {
77
+ console.error(`Slice "${sliceId}" has no variations.\n`);
78
+ console.error("Add a variation first before adding fields.");
79
+ process.exitCode = 1;
80
+ return;
81
+ }
82
+
83
+ // Find target variation
84
+ const targetVariation = variation
85
+ ? model.variations.find((v) => v.id === variation)
86
+ : model.variations[0];
87
+
88
+ if (!targetVariation) {
89
+ console.error(`Variation "${variation}" not found in slice "${sliceId}"\n`);
90
+ console.error(`Available variations: ${model.variations.map((v) => v.id).join(", ")}`);
91
+ process.exitCode = 1;
92
+ return;
93
+ }
94
+
95
+ // Initialize primary if it doesn't exist
96
+ if (!targetVariation.primary) {
97
+ targetVariation.primary = {};
98
+ }
99
+
100
+ // Check if field already exists in any variation
101
+ for (const v of model.variations) {
102
+ if (v.primary?.[fieldId]) {
103
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
104
+ process.exitCode = 1;
105
+ return;
106
+ }
107
+ }
108
+
109
+ // Build field definition
110
+ const fieldDefinition: Color = {
111
+ type: "Color",
112
+ config: {
113
+ ...(label && { label }),
114
+ ...(placeholder && { placeholder }),
115
+ },
116
+ };
117
+
118
+ // Add field to variation
119
+ targetVariation.primary[fieldId] = fieldDefinition;
120
+
121
+ // Write updated model
122
+ try {
123
+ await writeFile(modelPath, stringify(model as SharedSlice));
124
+ } catch (error) {
125
+ if (error instanceof Error) {
126
+ console.error(`Failed to update slice: ${error.message}`);
127
+ } else {
128
+ console.error("Failed to update slice");
129
+ }
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+
134
+ console.info(
135
+ `Added field "${fieldId}" (Color) to "${targetVariation.id}" variation in ${sliceId}`,
136
+ );
137
+ }
@@ -0,0 +1,137 @@
1
+ import type { Date as DateField, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { writeFile } from "node:fs/promises";
4
+ import { parseArgs } from "node:util";
5
+
6
+ import { stringify } from "./lib/json";
7
+ import { findSliceModel } from "./lib/slice";
8
+
9
+ const HELP = `
10
+ Add a date picker field to an existing slice.
11
+
12
+ USAGE
13
+ prismic slice add-field date <slice-id> <field-id> [flags]
14
+
15
+ ARGUMENTS
16
+ slice-id Slice identifier (required)
17
+ field-id Field identifier (required)
18
+
19
+ FLAGS
20
+ -v, --variation string Target variation (default: first variation)
21
+ -l, --label string Display label for the field
22
+ -p, --placeholder string Placeholder text
23
+ -h, --help Show help for command
24
+
25
+ EXAMPLES
26
+ prismic slice add-field date my_slice publish_date
27
+ prismic slice add-field date event start_date --label "Start Date"
28
+ prismic slice add-field date promo end_date --variation "countdown"
29
+ `.trim();
30
+
31
+ export async function sliceAddFieldDate(): Promise<void> {
32
+ const {
33
+ values: { help, variation, label, placeholder },
34
+ positionals: [sliceId, fieldId],
35
+ } = parseArgs({
36
+ args: process.argv.slice(5), // skip: node, script, "slice", "add-field", "date"
37
+ options: {
38
+ variation: { type: "string", short: "v" },
39
+ label: { type: "string", short: "l" },
40
+ placeholder: { type: "string", short: "p" },
41
+ help: { type: "boolean", short: "h" },
42
+ },
43
+ allowPositionals: true,
44
+ });
45
+
46
+ if (help) {
47
+ console.info(HELP);
48
+ return;
49
+ }
50
+
51
+ if (!sliceId) {
52
+ console.error("Missing required argument: slice-id\n");
53
+ console.error("Usage: prismic slice add-field date <slice-id> <field-id>");
54
+ process.exitCode = 1;
55
+ return;
56
+ }
57
+
58
+ if (!fieldId) {
59
+ console.error("Missing required argument: field-id\n");
60
+ console.error("Usage: prismic slice add-field date <slice-id> <field-id>");
61
+ process.exitCode = 1;
62
+ return;
63
+ }
64
+
65
+ // Find the slice model
66
+ const result = await findSliceModel(sliceId);
67
+ if (!result.ok) {
68
+ console.error(result.error);
69
+ process.exitCode = 1;
70
+ return;
71
+ }
72
+
73
+ const { model, modelPath } = result;
74
+
75
+ // Check for variations
76
+ if (model.variations.length === 0) {
77
+ console.error(`Slice "${sliceId}" has no variations.\n`);
78
+ console.error("Add a variation first before adding fields.");
79
+ process.exitCode = 1;
80
+ return;
81
+ }
82
+
83
+ // Find target variation
84
+ const targetVariation = variation
85
+ ? model.variations.find((v) => v.id === variation)
86
+ : model.variations[0];
87
+
88
+ if (!targetVariation) {
89
+ console.error(`Variation "${variation}" not found in slice "${sliceId}"\n`);
90
+ console.error(`Available variations: ${model.variations.map((v) => v.id).join(", ")}`);
91
+ process.exitCode = 1;
92
+ return;
93
+ }
94
+
95
+ // Initialize primary if it doesn't exist
96
+ if (!targetVariation.primary) {
97
+ targetVariation.primary = {};
98
+ }
99
+
100
+ // Check if field already exists in any variation
101
+ for (const v of model.variations) {
102
+ if (v.primary?.[fieldId]) {
103
+ console.error(`Field "${fieldId}" already exists in variation "${v.id}"`);
104
+ process.exitCode = 1;
105
+ return;
106
+ }
107
+ }
108
+
109
+ // Build field definition
110
+ const fieldDefinition: DateField = {
111
+ type: "Date",
112
+ config: {
113
+ ...(label && { label }),
114
+ ...(placeholder && { placeholder }),
115
+ },
116
+ };
117
+
118
+ // Add field to variation
119
+ targetVariation.primary[fieldId] = fieldDefinition;
120
+
121
+ // Write updated model
122
+ try {
123
+ await writeFile(modelPath, stringify(model as SharedSlice));
124
+ } catch (error) {
125
+ if (error instanceof Error) {
126
+ console.error(`Failed to update slice: ${error.message}`);
127
+ } else {
128
+ console.error("Failed to update slice");
129
+ }
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+
134
+ console.info(
135
+ `Added field "${fieldId}" (Date) to "${targetVariation.id}" variation in ${sliceId}`,
136
+ );
137
+ }