@angeloashmore/prismic-cli-poc 0.0.0-pr.8.b80fefa → 0.0.0-pr.9.5366ece

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 (58) hide show
  1. package/dist/index.mjs +302 -168
  2. package/package.json +1 -1
  3. package/src/custom-type-add-field-boolean.ts +49 -12
  4. package/src/custom-type-add-field-color.ts +46 -12
  5. package/src/custom-type-add-field-date.ts +46 -12
  6. package/src/custom-type-add-field-embed.ts +46 -12
  7. package/src/custom-type-add-field-geo-point.ts +46 -12
  8. package/src/custom-type-add-field-group.ts +179 -0
  9. package/src/custom-type-add-field-image.ts +46 -12
  10. package/src/custom-type-add-field-key-text.ts +46 -12
  11. package/src/custom-type-add-field-link.ts +46 -12
  12. package/src/custom-type-add-field-number.ts +46 -12
  13. package/src/custom-type-add-field-rich-text.ts +46 -12
  14. package/src/custom-type-add-field-select.ts +47 -21
  15. package/src/custom-type-add-field-timestamp.ts +46 -12
  16. package/src/custom-type-add-field-uid.ts +17 -0
  17. package/src/custom-type-add-field.ts +5 -0
  18. package/src/index.ts +5 -0
  19. package/src/lib/field-path.ts +81 -0
  20. package/src/page-type-add-field-boolean.ts +66 -13
  21. package/src/page-type-add-field-color.ts +66 -13
  22. package/src/page-type-add-field-date.ts +66 -13
  23. package/src/page-type-add-field-embed.ts +66 -13
  24. package/src/page-type-add-field-geo-point.ts +66 -13
  25. package/src/page-type-add-field-group.ts +198 -0
  26. package/src/page-type-add-field-image.ts +66 -13
  27. package/src/page-type-add-field-key-text.ts +66 -13
  28. package/src/page-type-add-field-link.ts +66 -13
  29. package/src/page-type-add-field-number.ts +66 -13
  30. package/src/page-type-add-field-rich-text.ts +66 -13
  31. package/src/page-type-add-field-select.ts +67 -22
  32. package/src/page-type-add-field-timestamp.ts +66 -13
  33. package/src/page-type-add-field-uid.ts +37 -1
  34. package/src/page-type-add-field.ts +5 -0
  35. package/src/page-type-create.ts +25 -0
  36. package/src/repo-create.ts +59 -0
  37. package/src/skill-install.ts +177 -0
  38. package/src/skill-uninstall.ts +85 -0
  39. package/src/skill.ts +50 -0
  40. package/src/slice-add-field-boolean.ts +90 -16
  41. package/src/slice-add-field-color.ts +90 -16
  42. package/src/slice-add-field-date.ts +90 -16
  43. package/src/slice-add-field-embed.ts +90 -16
  44. package/src/slice-add-field-geo-point.ts +90 -16
  45. package/src/slice-add-field-group.ts +191 -0
  46. package/src/slice-add-field-image.ts +90 -16
  47. package/src/slice-add-field-key-text.ts +90 -16
  48. package/src/slice-add-field-link.ts +90 -16
  49. package/src/slice-add-field-number.ts +90 -16
  50. package/src/slice-add-field-rich-text.ts +90 -16
  51. package/src/slice-add-field-select.ts +91 -25
  52. package/src/slice-add-field-timestamp.ts +90 -16
  53. package/src/slice-add-field.ts +5 -0
  54. package/src/slice-create.ts +66 -5
  55. package/src/slice-set-screenshot.ts +235 -0
  56. package/src/slice-view.ts +3 -0
  57. package/src/slice.ts +5 -0
  58. package/src/status.ts +164 -124
@@ -0,0 +1,235 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { extname } from "node:path";
4
+ import { parseArgs } from "node:util";
5
+ import * as v from "valibot";
6
+
7
+ import { isAuthenticated } from "./lib/auth";
8
+ import { safeGetRepositoryFromConfig } from "./lib/config";
9
+ import { stringify } from "./lib/json";
10
+ import { request } from "./lib/request";
11
+ import { findSliceModel } from "./lib/slice";
12
+
13
+ const HELP = `
14
+ Set a screenshot for a slice variation.
15
+
16
+ USAGE
17
+ prismic slice set-screenshot <slice-id> <image-path> [flags]
18
+
19
+ ARGUMENTS
20
+ slice-id Slice identifier (required)
21
+ image-path Path to the image file (required)
22
+
23
+ FLAGS
24
+ -v, --variation string Variation ID (default: "default")
25
+ -r, --repo string Repository name (uses config if not provided)
26
+ -h, --help Show help for command
27
+
28
+ EXAMPLES
29
+ prismic slice set-screenshot MySlice ./screenshot.png
30
+ prismic slice set-screenshot MySlice ./screenshot.png --variation dark
31
+ prismic slice set-screenshot MySlice ./screenshot.png --repo my-repo
32
+ `.trim();
33
+
34
+ const ACLCreateResponseSchema = v.object({
35
+ uploadEndpoint: v.string(),
36
+ requiredFormDataFields: v.record(v.string(), v.string()),
37
+ imgixEndpoint: v.string(),
38
+ });
39
+
40
+ export type UploadScreenshotArgs = {
41
+ data: Buffer;
42
+ repo: string;
43
+ sliceId: string;
44
+ variationId: string;
45
+ filename: string;
46
+ };
47
+
48
+ export async function uploadScreenshot(args: UploadScreenshotArgs): Promise<string> {
49
+ const { data, repo, sliceId, variationId, filename } = args;
50
+
51
+ // Get upload credentials from ACL provider
52
+ const aclResponse = await request("https://acl-provider.prismic.io/create", {
53
+ method: "POST",
54
+ body: {},
55
+ schema: ACLCreateResponseSchema,
56
+ });
57
+
58
+ if (!aclResponse.ok) {
59
+ throw new Error("Failed to get upload credentials from Prismic");
60
+ }
61
+
62
+ const { uploadEndpoint, requiredFormDataFields, imgixEndpoint } = aclResponse.value;
63
+
64
+ // Generate content digest for unique filename
65
+ const digest = createHash("md5").update(data).digest("hex");
66
+ const ext = extname(filename).toLowerCase().slice(1) || "png";
67
+
68
+ // Build the S3 key path
69
+ const key = `${repo}/shared-slices/${sliceId}/${variationId}/${digest}.${ext}`;
70
+
71
+ // Create FormData for S3 upload
72
+ const formData = new FormData();
73
+ for (const [field, value] of Object.entries(requiredFormDataFields)) {
74
+ formData.append(field, value);
75
+ }
76
+ formData.append("key", key);
77
+ formData.append("Content-Type", getMimeType(ext));
78
+ formData.append("file", new Blob([new Uint8Array(data)], { type: getMimeType(ext) }), filename);
79
+
80
+ // Upload to S3
81
+ const uploadResponse = await fetch(uploadEndpoint, {
82
+ method: "POST",
83
+ body: formData,
84
+ });
85
+
86
+ if (!uploadResponse.ok) {
87
+ const text = await uploadResponse.text();
88
+ throw new Error(`Failed to upload screenshot: ${text}`);
89
+ }
90
+
91
+ // Construct Imgix URL
92
+ const imgixUrl = `${imgixEndpoint}/${key}?auto=compress,format`;
93
+
94
+ return imgixUrl;
95
+ }
96
+
97
+ function getMimeType(ext: string): string {
98
+ switch (ext) {
99
+ case "png":
100
+ return "image/png";
101
+ case "jpg":
102
+ case "jpeg":
103
+ return "image/jpeg";
104
+ case "gif":
105
+ return "image/gif";
106
+ case "webp":
107
+ return "image/webp";
108
+ default:
109
+ return "image/png";
110
+ }
111
+ }
112
+
113
+ export async function sliceSetScreenshot(): Promise<void> {
114
+ const {
115
+ values: { help, variation, repo: repoFlag },
116
+ positionals: [sliceId, imagePath],
117
+ } = parseArgs({
118
+ args: process.argv.slice(4), // skip: node, script, "slice", "set-screenshot"
119
+ options: {
120
+ variation: { type: "string", short: "v", default: "default" },
121
+ repo: { type: "string", short: "r" },
122
+ help: { type: "boolean", short: "h" },
123
+ },
124
+ allowPositionals: true,
125
+ });
126
+
127
+ if (help) {
128
+ console.info(HELP);
129
+ return;
130
+ }
131
+
132
+ if (!sliceId) {
133
+ console.error("Missing required argument: slice-id\n");
134
+ console.error("Usage: prismic slice set-screenshot <slice-id> <image-path>");
135
+ process.exitCode = 1;
136
+ return;
137
+ }
138
+
139
+ if (!imagePath) {
140
+ console.error("Missing required argument: image-path\n");
141
+ console.error("Usage: prismic slice set-screenshot <slice-id> <image-path>");
142
+ process.exitCode = 1;
143
+ return;
144
+ }
145
+
146
+ // Check authentication
147
+ const authenticated = await isAuthenticated();
148
+ if (!authenticated) {
149
+ console.error("You must be logged in to set a screenshot.");
150
+ console.error("Run `prismic login` to authenticate.");
151
+ process.exitCode = 1;
152
+ return;
153
+ }
154
+
155
+ // Resolve repository
156
+ const repo = repoFlag ?? (await safeGetRepositoryFromConfig());
157
+ if (!repo) {
158
+ console.error("Could not determine repository.");
159
+ console.error("Use --repo flag or run from a directory with prismic.config.json");
160
+ process.exitCode = 1;
161
+ return;
162
+ }
163
+
164
+ // Find the slice model
165
+ const result = await findSliceModel(sliceId);
166
+ if (!result.ok) {
167
+ console.error(result.error);
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+
172
+ const { model, modelPath } = result;
173
+
174
+ // Find the variation
175
+ const variationIndex = model.variations.findIndex((v) => v.id === variation);
176
+ if (variationIndex === -1) {
177
+ console.error(`Variation "${variation}" not found in slice "${sliceId}"`);
178
+ console.error(`Available variations: ${model.variations.map((v) => v.id).join(", ")}`);
179
+ process.exitCode = 1;
180
+ return;
181
+ }
182
+
183
+ // Read the image file
184
+ let imageData: Buffer;
185
+ try {
186
+ imageData = await readFile(imagePath);
187
+ } catch (error) {
188
+ if (error instanceof Error) {
189
+ console.error(`Failed to read image file: ${error.message}`);
190
+ } else {
191
+ console.error("Failed to read image file");
192
+ }
193
+ process.exitCode = 1;
194
+ return;
195
+ }
196
+
197
+ // Upload the screenshot
198
+ let imageUrl: string;
199
+ try {
200
+ imageUrl = await uploadScreenshot({
201
+ data: imageData,
202
+ repo,
203
+ sliceId,
204
+ variationId: variation ?? "default",
205
+ filename: imagePath,
206
+ });
207
+ } catch (error) {
208
+ if (error instanceof Error) {
209
+ console.error(`Failed to upload screenshot: ${error.message}`);
210
+ } else {
211
+ console.error("Failed to upload screenshot");
212
+ }
213
+ process.exitCode = 1;
214
+ return;
215
+ }
216
+
217
+ // Update the model
218
+ model.variations[variationIndex].imageUrl = imageUrl;
219
+
220
+ // Write updated model
221
+ try {
222
+ await writeFile(modelPath, stringify(model));
223
+ } catch (error) {
224
+ if (error instanceof Error) {
225
+ console.error(`Failed to update slice model: ${error.message}`);
226
+ } else {
227
+ console.error("Failed to update slice model");
228
+ }
229
+ process.exitCode = 1;
230
+ return;
231
+ }
232
+
233
+ console.info(`Screenshot set for slice "${sliceId}" variation "${variation}"`);
234
+ console.info(`URL: ${imageUrl}`);
235
+ }
package/src/slice-view.ts CHANGED
@@ -73,5 +73,8 @@ export async function sliceView(): Promise<void> {
73
73
  console.info(
74
74
  ` - ${variation.id} (${variation.name}): ${primaryFields} primary fields, ${itemsFields} items fields`,
75
75
  );
76
+ if (variation.imageUrl) {
77
+ console.info(` Screenshot: ${variation.imageUrl}`);
78
+ }
76
79
  }
77
80
  }
package/src/slice.ts CHANGED
@@ -9,6 +9,7 @@ import { sliceRemove } from "./slice-remove";
9
9
  import { sliceRemoveField } from "./slice-remove-field";
10
10
  import { sliceRemoveVariation } from "./slice-remove-variation";
11
11
  import { sliceRename } from "./slice-rename";
12
+ import { sliceSetScreenshot } from "./slice-set-screenshot";
12
13
  import { sliceView } from "./slice-view";
13
14
 
14
15
  const HELP = `
@@ -28,6 +29,7 @@ COMMANDS
28
29
  add-variation Add a variation to a slice
29
30
  remove-variation Remove a variation from a slice
30
31
  list-variations List all variations of a slice
32
+ set-screenshot Set a screenshot for a slice variation
31
33
 
32
34
  FLAGS
33
35
  -h, --help Show help for command
@@ -79,6 +81,9 @@ export async function slice(): Promise<void> {
79
81
  case "list-variations":
80
82
  await sliceListVariations();
81
83
  break;
84
+ case "set-screenshot":
85
+ await sliceSetScreenshot();
86
+ break;
82
87
  default: {
83
88
  if (subcommand) {
84
89
  console.error(`Unknown slice subcommand: ${subcommand}\n`);