@angeloashmore/prismic-cli-poc 0.0.0-canary.1d36cd8
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.
- package/LICENSE +202 -0
- package/README.md +98 -0
- package/dist/index.mjs +2548 -0
- package/package.json +53 -0
- package/src/codegen-types.ts +82 -0
- package/src/codegen.ts +45 -0
- package/src/custom-type-add-field-boolean.ts +192 -0
- package/src/custom-type-add-field-color.ts +177 -0
- package/src/custom-type-add-field-date.ts +180 -0
- package/src/custom-type-add-field-embed.ts +177 -0
- package/src/custom-type-add-field-geo-point.ts +174 -0
- package/src/custom-type-add-field-image.ts +177 -0
- package/src/custom-type-add-field-key-text.ts +177 -0
- package/src/custom-type-add-field-link.ts +201 -0
- package/src/custom-type-add-field-number.ts +209 -0
- package/src/custom-type-add-field-rich-text.ts +202 -0
- package/src/custom-type-add-field-select.ts +192 -0
- package/src/custom-type-add-field-timestamp.ts +180 -0
- package/src/custom-type-add-field-uid.ts +177 -0
- package/src/custom-type-add-field.ts +111 -0
- package/src/custom-type-connect-slice.ts +220 -0
- package/src/custom-type-create.ts +118 -0
- package/src/custom-type-disconnect-slice.ts +177 -0
- package/src/custom-type-list.ts +110 -0
- package/src/custom-type-remove-field.ts +177 -0
- package/src/custom-type-remove.ts +144 -0
- package/src/custom-type-set-name.ts +144 -0
- package/src/custom-type-view.ts +118 -0
- package/src/custom-type.ts +85 -0
- package/src/index.ts +127 -0
- package/src/init.ts +64 -0
- package/src/lib/auth.ts +83 -0
- package/src/lib/config.ts +111 -0
- package/src/lib/custom-types-api.ts +438 -0
- package/src/lib/file.ts +49 -0
- package/src/lib/framework.ts +143 -0
- package/src/lib/json.ts +3 -0
- package/src/lib/request.ts +116 -0
- package/src/lib/slice.ts +115 -0
- package/src/lib/string.ts +6 -0
- package/src/lib/url.ts +25 -0
- package/src/locale-add.ts +116 -0
- package/src/locale-list.ts +107 -0
- package/src/locale-remove.ts +88 -0
- package/src/locale-set-default.ts +131 -0
- package/src/locale.ts +60 -0
- package/src/login.ts +152 -0
- package/src/logout.ts +36 -0
- package/src/page-type-add-field-boolean.ts +192 -0
- package/src/page-type-add-field-color.ts +177 -0
- package/src/page-type-add-field-date.ts +180 -0
- package/src/page-type-add-field-embed.ts +177 -0
- package/src/page-type-add-field-geo-point.ts +174 -0
- package/src/page-type-add-field-image.ts +177 -0
- package/src/page-type-add-field-key-text.ts +177 -0
- package/src/page-type-add-field-link.ts +201 -0
- package/src/page-type-add-field-number.ts +209 -0
- package/src/page-type-add-field-rich-text.ts +202 -0
- package/src/page-type-add-field-select.ts +192 -0
- package/src/page-type-add-field-timestamp.ts +180 -0
- package/src/page-type-add-field-uid.ts +177 -0
- package/src/page-type-add-field.ts +111 -0
- package/src/page-type-connect-slice.ts +220 -0
- package/src/page-type-create.ts +142 -0
- package/src/page-type-disconnect-slice.ts +177 -0
- package/src/page-type-list.ts +109 -0
- package/src/page-type-remove-field.ts +177 -0
- package/src/page-type-remove.ts +144 -0
- package/src/page-type-set-name.ts +144 -0
- package/src/page-type-set-repeatable.ts +153 -0
- package/src/page-type-view.ts +118 -0
- package/src/page-type.ts +90 -0
- package/src/preview-add.ts +126 -0
- package/src/preview-get-simulator.ts +104 -0
- package/src/preview-list.ts +106 -0
- package/src/preview-remove-simulator.ts +80 -0
- package/src/preview-remove.ts +109 -0
- package/src/preview-set-name.ts +137 -0
- package/src/preview-set-simulator.ts +116 -0
- package/src/preview.ts +75 -0
- package/src/pull.ts +247 -0
- package/src/push.ts +405 -0
- package/src/repo-create.ts +136 -0
- package/src/repo-get-access.ts +86 -0
- package/src/repo-list.ts +100 -0
- package/src/repo-set-access.ts +100 -0
- package/src/repo-set-name.ts +102 -0
- package/src/repo-view.ts +113 -0
- package/src/repo.ts +70 -0
- package/src/slice-add-field-boolean.ts +173 -0
- package/src/slice-add-field-color.ts +158 -0
- package/src/slice-add-field-date.ts +158 -0
- package/src/slice-add-field-embed.ts +158 -0
- package/src/slice-add-field-geo-point.ts +155 -0
- package/src/slice-add-field-image.ts +155 -0
- package/src/slice-add-field-key-text.ts +158 -0
- package/src/slice-add-field-link.ts +178 -0
- package/src/slice-add-field-number.ts +158 -0
- package/src/slice-add-field-rich-text.ts +183 -0
- package/src/slice-add-field-select.ts +173 -0
- package/src/slice-add-field-timestamp.ts +158 -0
- package/src/slice-add-field.ts +106 -0
- package/src/slice-add-variation.ts +145 -0
- package/src/slice-create.ts +148 -0
- package/src/slice-list-variations.ts +67 -0
- package/src/slice-list.ts +88 -0
- package/src/slice-remove-field.ts +128 -0
- package/src/slice-remove-variation.ts +118 -0
- package/src/slice-remove.ts +97 -0
- package/src/slice-rename.ts +128 -0
- package/src/slice-view.ts +77 -0
- package/src/slice.ts +90 -0
- package/src/status.ts +733 -0
- package/src/token-create.ts +203 -0
- package/src/token-delete.ts +182 -0
- package/src/token-list.ts +223 -0
- package/src/token-set-name.ts +193 -0
- package/src/token.ts +60 -0
- package/src/webhook-add-header.ts +118 -0
- package/src/webhook-create.ts +152 -0
- package/src/webhook-disable.ts +109 -0
- package/src/webhook-enable.ts +132 -0
- package/src/webhook-list.ts +93 -0
- package/src/webhook-remove-header.ts +117 -0
- package/src/webhook-remove.ts +106 -0
- package/src/webhook-set-triggers.ts +148 -0
- package/src/webhook-status.ts +90 -0
- package/src/webhook-test.ts +106 -0
- package/src/webhook-view.ts +147 -0
- package/src/webhook.ts +95 -0
- package/src/whoami.ts +62 -0
package/src/push.ts
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import type { CustomType, SharedSlice } from "@prismicio/types-internal/lib/customtypes";
|
|
2
|
+
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
|
|
5
|
+
import { isAuthenticated } from "./lib/auth";
|
|
6
|
+
import { safeGetRepositoryFromConfig } from "./lib/config";
|
|
7
|
+
import {
|
|
8
|
+
deleteCustomType,
|
|
9
|
+
deleteSlice,
|
|
10
|
+
fetchRemoteCustomTypes,
|
|
11
|
+
fetchRemoteSlices,
|
|
12
|
+
insertCustomType,
|
|
13
|
+
insertSlice,
|
|
14
|
+
readLocalCustomTypes,
|
|
15
|
+
readLocalSlices,
|
|
16
|
+
updateCustomType,
|
|
17
|
+
updateSlice,
|
|
18
|
+
} from "./lib/custom-types-api";
|
|
19
|
+
import { stringify } from "./lib/json";
|
|
20
|
+
|
|
21
|
+
const HELP = `
|
|
22
|
+
Push custom types and slices to Prismic from local files.
|
|
23
|
+
|
|
24
|
+
By default, this command reads the repository from prismic.config.json at the
|
|
25
|
+
project root.
|
|
26
|
+
|
|
27
|
+
USAGE
|
|
28
|
+
prismic push [flags]
|
|
29
|
+
|
|
30
|
+
FLAGS
|
|
31
|
+
-r, --repo string Repository domain
|
|
32
|
+
--dry-run Show what would be pushed without making changes
|
|
33
|
+
--types-only Only push custom types
|
|
34
|
+
--slices-only Only push slices
|
|
35
|
+
--delete Delete remote models that don't exist locally (dangerous)
|
|
36
|
+
--json Output as JSON
|
|
37
|
+
-h, --help Show help for command
|
|
38
|
+
|
|
39
|
+
EXAMPLES
|
|
40
|
+
prismic push
|
|
41
|
+
prismic push --repo my-repo
|
|
42
|
+
prismic push --dry-run
|
|
43
|
+
prismic push --types-only
|
|
44
|
+
prismic push --delete
|
|
45
|
+
`.trim();
|
|
46
|
+
|
|
47
|
+
type DiffResult<T> = {
|
|
48
|
+
toInsert: T[];
|
|
49
|
+
toUpdate: T[];
|
|
50
|
+
toDelete: string[];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function computeDiff<T extends { id: string }>(local: T[], remote: T[]): DiffResult<T> {
|
|
54
|
+
const localById = new Map(local.map((item) => [item.id, item]));
|
|
55
|
+
const remoteById = new Map(remote.map((item) => [item.id, item]));
|
|
56
|
+
|
|
57
|
+
const toInsert: T[] = [];
|
|
58
|
+
const toUpdate: T[] = [];
|
|
59
|
+
const toDelete: string[] = [];
|
|
60
|
+
|
|
61
|
+
// Find items to insert or update
|
|
62
|
+
for (const localItem of local) {
|
|
63
|
+
const remoteItem = remoteById.get(localItem.id);
|
|
64
|
+
if (!remoteItem) {
|
|
65
|
+
toInsert.push(localItem);
|
|
66
|
+
} else if (JSON.stringify(localItem) !== JSON.stringify(remoteItem)) {
|
|
67
|
+
toUpdate.push(localItem);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Find items to delete (remote IDs not in local)
|
|
72
|
+
for (const remoteItem of remote) {
|
|
73
|
+
if (!localById.has(remoteItem.id)) {
|
|
74
|
+
toDelete.push(remoteItem.id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { toInsert, toUpdate, toDelete };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function push(): Promise<void> {
|
|
82
|
+
const {
|
|
83
|
+
values: {
|
|
84
|
+
help,
|
|
85
|
+
repo = await safeGetRepositoryFromConfig(),
|
|
86
|
+
"dry-run": dryRun,
|
|
87
|
+
"types-only": typesOnly,
|
|
88
|
+
"slices-only": slicesOnly,
|
|
89
|
+
delete: deleteRemote,
|
|
90
|
+
json,
|
|
91
|
+
},
|
|
92
|
+
} = parseArgs({
|
|
93
|
+
args: process.argv.slice(3), // skip: node, script, "push"
|
|
94
|
+
options: {
|
|
95
|
+
repo: { type: "string", short: "r" },
|
|
96
|
+
"dry-run": { type: "boolean" },
|
|
97
|
+
"types-only": { type: "boolean" },
|
|
98
|
+
"slices-only": { type: "boolean" },
|
|
99
|
+
delete: { type: "boolean" },
|
|
100
|
+
json: { type: "boolean" },
|
|
101
|
+
help: { type: "boolean", short: "h" },
|
|
102
|
+
},
|
|
103
|
+
allowPositionals: false,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (help) {
|
|
107
|
+
console.info(HELP);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!repo) {
|
|
112
|
+
console.error("Missing prismic.config.json or --repo option");
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check authentication
|
|
118
|
+
if (!(await isAuthenticated())) {
|
|
119
|
+
console.error("Not logged in. Run `prismic login` first.");
|
|
120
|
+
process.exitCode = 1;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!json) {
|
|
125
|
+
console.info(`Pushing to repository: ${repo}\n`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const shouldPushTypes = !slicesOnly;
|
|
129
|
+
const shouldPushSlices = !typesOnly;
|
|
130
|
+
|
|
131
|
+
// Read local and fetch remote data in parallel
|
|
132
|
+
const [localTypesResult, localSlicesResult, remoteTypesResult, remoteSlicesResult] =
|
|
133
|
+
await Promise.all([
|
|
134
|
+
shouldPushTypes ? readLocalCustomTypes() : Promise.resolve({ ok: true, value: [] } as const),
|
|
135
|
+
shouldPushSlices ? readLocalSlices() : Promise.resolve({ ok: true, value: [] } as const),
|
|
136
|
+
shouldPushTypes
|
|
137
|
+
? fetchRemoteCustomTypes(repo)
|
|
138
|
+
: Promise.resolve({ ok: true, value: [] } as const),
|
|
139
|
+
shouldPushSlices
|
|
140
|
+
? fetchRemoteSlices(repo)
|
|
141
|
+
: Promise.resolve({ ok: true, value: [] } as const),
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
if (!localTypesResult.ok) {
|
|
145
|
+
console.error(`Failed to read local custom types: ${localTypesResult.error}`);
|
|
146
|
+
process.exitCode = 1;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!localSlicesResult.ok) {
|
|
151
|
+
console.error(`Failed to read local slices: ${localSlicesResult.error}`);
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!remoteTypesResult.ok) {
|
|
157
|
+
console.error(`Failed to fetch remote custom types: ${remoteTypesResult.error}`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!remoteSlicesResult.ok) {
|
|
163
|
+
console.error(`Failed to fetch remote slices: ${remoteSlicesResult.error}`);
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const localTypes = localTypesResult.value;
|
|
169
|
+
const localSlices = localSlicesResult.value;
|
|
170
|
+
const remoteTypes = remoteTypesResult.value;
|
|
171
|
+
const remoteSlices = remoteSlicesResult.value;
|
|
172
|
+
|
|
173
|
+
if (!json) {
|
|
174
|
+
if (shouldPushTypes) {
|
|
175
|
+
console.info(`Local custom types: ${localTypes.length}`);
|
|
176
|
+
console.info(`Remote custom types: ${remoteTypes.length}`);
|
|
177
|
+
}
|
|
178
|
+
if (shouldPushSlices) {
|
|
179
|
+
console.info(`Local slices: ${localSlices.length}`);
|
|
180
|
+
console.info(`Remote slices: ${remoteSlices.length}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Compute diffs
|
|
185
|
+
const typesDiff = shouldPushTypes
|
|
186
|
+
? computeDiff([...localTypes], [...remoteTypes])
|
|
187
|
+
: { toInsert: [], toUpdate: [], toDelete: [] };
|
|
188
|
+
const slicesDiff = shouldPushSlices
|
|
189
|
+
? computeDiff([...localSlices], [...remoteSlices])
|
|
190
|
+
: { toInsert: [], toUpdate: [], toDelete: [] };
|
|
191
|
+
|
|
192
|
+
// If --delete is not specified, clear the toDelete arrays
|
|
193
|
+
if (!deleteRemote) {
|
|
194
|
+
typesDiff.toDelete = [];
|
|
195
|
+
slicesDiff.toDelete = [];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const totalChanges =
|
|
199
|
+
typesDiff.toInsert.length +
|
|
200
|
+
typesDiff.toUpdate.length +
|
|
201
|
+
typesDiff.toDelete.length +
|
|
202
|
+
slicesDiff.toInsert.length +
|
|
203
|
+
slicesDiff.toUpdate.length +
|
|
204
|
+
slicesDiff.toDelete.length;
|
|
205
|
+
|
|
206
|
+
if (totalChanges === 0) {
|
|
207
|
+
if (json) {
|
|
208
|
+
console.info(
|
|
209
|
+
stringify({
|
|
210
|
+
customTypes: { inserted: [], updated: [], deleted: [] },
|
|
211
|
+
slices: { inserted: [], updated: [], deleted: [] },
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
} else {
|
|
215
|
+
console.info("\nNo changes to push.");
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Dry run - show what would happen
|
|
221
|
+
if (dryRun) {
|
|
222
|
+
if (json) {
|
|
223
|
+
console.info(
|
|
224
|
+
stringify({
|
|
225
|
+
customTypes: {
|
|
226
|
+
toInsert: typesDiff.toInsert.map((t) => t.id),
|
|
227
|
+
toUpdate: typesDiff.toUpdate.map((t) => t.id),
|
|
228
|
+
toDelete: typesDiff.toDelete,
|
|
229
|
+
},
|
|
230
|
+
slices: {
|
|
231
|
+
toInsert: slicesDiff.toInsert.map((s) => s.id),
|
|
232
|
+
toUpdate: slicesDiff.toUpdate.map((s) => s.id),
|
|
233
|
+
toDelete: slicesDiff.toDelete,
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
237
|
+
} else {
|
|
238
|
+
console.info("");
|
|
239
|
+
if (shouldPushTypes) {
|
|
240
|
+
if (typesDiff.toInsert.length > 0) {
|
|
241
|
+
console.info("Would insert custom types:");
|
|
242
|
+
for (const ct of typesDiff.toInsert) {
|
|
243
|
+
console.info(` + ${ct.id}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (typesDiff.toUpdate.length > 0) {
|
|
247
|
+
console.info("Would update custom types:");
|
|
248
|
+
for (const ct of typesDiff.toUpdate) {
|
|
249
|
+
console.info(` ~ ${ct.id}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (typesDiff.toDelete.length > 0) {
|
|
253
|
+
console.info("Would delete custom types:");
|
|
254
|
+
for (const id of typesDiff.toDelete) {
|
|
255
|
+
console.info(` - ${id}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (shouldPushSlices) {
|
|
260
|
+
if (slicesDiff.toInsert.length > 0) {
|
|
261
|
+
console.info("Would insert slices:");
|
|
262
|
+
for (const slice of slicesDiff.toInsert) {
|
|
263
|
+
console.info(` + ${slice.id}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (slicesDiff.toUpdate.length > 0) {
|
|
267
|
+
console.info("Would update slices:");
|
|
268
|
+
for (const slice of slicesDiff.toUpdate) {
|
|
269
|
+
console.info(` ~ ${slice.id}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (slicesDiff.toDelete.length > 0) {
|
|
273
|
+
console.info("Would delete slices:");
|
|
274
|
+
for (const id of slicesDiff.toDelete) {
|
|
275
|
+
console.info(` - ${id}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
console.info(`\nDry run complete: ${totalChanges} changes would be made`);
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Execute changes
|
|
285
|
+
const results = {
|
|
286
|
+
customTypes: { inserted: [] as string[], updated: [] as string[], deleted: [] as string[] },
|
|
287
|
+
slices: { inserted: [] as string[], updated: [] as string[], deleted: [] as string[] },
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Push custom types
|
|
291
|
+
if (shouldPushTypes) {
|
|
292
|
+
if (
|
|
293
|
+
!json &&
|
|
294
|
+
(typesDiff.toInsert.length > 0 ||
|
|
295
|
+
typesDiff.toUpdate.length > 0 ||
|
|
296
|
+
typesDiff.toDelete.length > 0)
|
|
297
|
+
) {
|
|
298
|
+
console.info("\nPushing custom types:");
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for (const ct of typesDiff.toInsert) {
|
|
302
|
+
const result = await insertCustomType(repo, ct as CustomType);
|
|
303
|
+
if (!result.ok) {
|
|
304
|
+
console.error(`Failed to insert custom type ${ct.id}: ${result.error}`);
|
|
305
|
+
process.exitCode = 1;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
results.customTypes.inserted.push(ct.id);
|
|
309
|
+
if (!json) {
|
|
310
|
+
console.info(` + ${ct.id}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
for (const ct of typesDiff.toUpdate) {
|
|
315
|
+
const result = await updateCustomType(repo, ct as CustomType);
|
|
316
|
+
if (!result.ok) {
|
|
317
|
+
console.error(`Failed to update custom type ${ct.id}: ${result.error}`);
|
|
318
|
+
process.exitCode = 1;
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
results.customTypes.updated.push(ct.id);
|
|
322
|
+
if (!json) {
|
|
323
|
+
console.info(` ~ ${ct.id}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const id of typesDiff.toDelete) {
|
|
328
|
+
const result = await deleteCustomType(repo, id);
|
|
329
|
+
if (!result.ok) {
|
|
330
|
+
console.error(`Failed to delete custom type ${id}: ${result.error}`);
|
|
331
|
+
process.exitCode = 1;
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
results.customTypes.deleted.push(id);
|
|
335
|
+
if (!json) {
|
|
336
|
+
console.info(` - ${id}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Push slices
|
|
342
|
+
if (shouldPushSlices) {
|
|
343
|
+
if (
|
|
344
|
+
!json &&
|
|
345
|
+
(slicesDiff.toInsert.length > 0 ||
|
|
346
|
+
slicesDiff.toUpdate.length > 0 ||
|
|
347
|
+
slicesDiff.toDelete.length > 0)
|
|
348
|
+
) {
|
|
349
|
+
console.info("\nPushing slices:");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
for (const slice of slicesDiff.toInsert) {
|
|
353
|
+
const result = await insertSlice(repo, slice as SharedSlice);
|
|
354
|
+
if (!result.ok) {
|
|
355
|
+
console.error(`Failed to insert slice ${slice.id}: ${result.error}`);
|
|
356
|
+
process.exitCode = 1;
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
results.slices.inserted.push(slice.id);
|
|
360
|
+
if (!json) {
|
|
361
|
+
console.info(` + ${slice.id}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
for (const slice of slicesDiff.toUpdate) {
|
|
366
|
+
const result = await updateSlice(repo, slice as SharedSlice);
|
|
367
|
+
if (!result.ok) {
|
|
368
|
+
console.error(`Failed to update slice ${slice.id}: ${result.error}`);
|
|
369
|
+
process.exitCode = 1;
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
results.slices.updated.push(slice.id);
|
|
373
|
+
if (!json) {
|
|
374
|
+
console.info(` ~ ${slice.id}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (const id of slicesDiff.toDelete) {
|
|
379
|
+
const result = await deleteSlice(repo, id);
|
|
380
|
+
if (!result.ok) {
|
|
381
|
+
console.error(`Failed to delete slice ${id}: ${result.error}`);
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
results.slices.deleted.push(id);
|
|
386
|
+
if (!json) {
|
|
387
|
+
console.info(` - ${id}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Output summary
|
|
393
|
+
if (json) {
|
|
394
|
+
console.info(stringify(results));
|
|
395
|
+
} else {
|
|
396
|
+
const totalPushed =
|
|
397
|
+
results.customTypes.inserted.length +
|
|
398
|
+
results.customTypes.updated.length +
|
|
399
|
+
results.customTypes.deleted.length +
|
|
400
|
+
results.slices.inserted.length +
|
|
401
|
+
results.slices.updated.length +
|
|
402
|
+
results.slices.deleted.length;
|
|
403
|
+
console.info(`\nPush complete: ${totalPushed} changes`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
|
|
3
|
+
import { isAuthenticated, readHost } from "./lib/auth";
|
|
4
|
+
import { createConfig, readConfig, updateConfig } from "./lib/config";
|
|
5
|
+
import { stringify } from "./lib/json";
|
|
6
|
+
import { ForbiddenRequestError, request } from "./lib/request";
|
|
7
|
+
import { getRepoUrl } from "./lib/url";
|
|
8
|
+
|
|
9
|
+
const HELP = `
|
|
10
|
+
Create a new Prismic repository.
|
|
11
|
+
|
|
12
|
+
Creates prismic.config.json in the current directory. If a config file already
|
|
13
|
+
exists, use --replace to update it with the new repository.
|
|
14
|
+
|
|
15
|
+
USAGE
|
|
16
|
+
prismic repo create <domain> [flags]
|
|
17
|
+
|
|
18
|
+
ARGUMENTS
|
|
19
|
+
domain Repository domain (required). Must be at least 4 characters,
|
|
20
|
+
start and end with alphanumeric, and contain only alphanumerics and hyphens.
|
|
21
|
+
|
|
22
|
+
FLAGS
|
|
23
|
+
-n, --name string Display name for the repository (defaults to domain)
|
|
24
|
+
--no-config Skip creating or updating prismic.config.json
|
|
25
|
+
--replace Replace existing repositoryName in prismic.config.json
|
|
26
|
+
-h, --help Show help for command
|
|
27
|
+
|
|
28
|
+
LEARN MORE
|
|
29
|
+
Use \`prismic repo <command> --help\` for more information about a command.
|
|
30
|
+
`.trim();
|
|
31
|
+
|
|
32
|
+
const DOMAIN_REGEX = /^[a-zA-Z0-9][-a-zA-Z0-9]{2,}[a-zA-Z0-9]$/;
|
|
33
|
+
|
|
34
|
+
export async function repoCreate(): Promise<void> {
|
|
35
|
+
const {
|
|
36
|
+
values: { help, name, "no-config": noConfig, replace },
|
|
37
|
+
positionals: [domain],
|
|
38
|
+
} = parseArgs({
|
|
39
|
+
args: process.argv.slice(4), // skip: node, script, "repo", "create"
|
|
40
|
+
options: {
|
|
41
|
+
help: { type: "boolean", short: "h" },
|
|
42
|
+
name: { type: "string", short: "n" },
|
|
43
|
+
"no-config": { type: "boolean" },
|
|
44
|
+
replace: { type: "boolean" },
|
|
45
|
+
},
|
|
46
|
+
allowPositionals: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (help) {
|
|
50
|
+
console.info(HELP);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!domain) {
|
|
55
|
+
console.error("Missing required argument: domain");
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!DOMAIN_REGEX.test(domain)) {
|
|
61
|
+
console.error("Invalid domain format.");
|
|
62
|
+
console.error(
|
|
63
|
+
"Must be at least 4 characters, start and end with alphanumeric, and contain only alphanumerics and hyphens.",
|
|
64
|
+
);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const authenticated = await isAuthenticated();
|
|
70
|
+
if (!authenticated) {
|
|
71
|
+
handleUnauthenticated();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check existing config before repo creation (unless --no-config)
|
|
76
|
+
const existingConfig = await readConfig();
|
|
77
|
+
if (!noConfig && existingConfig.ok && !replace) {
|
|
78
|
+
console.error(`This project already has a repository: ${existingConfig.config.repositoryName}`);
|
|
79
|
+
console.error("Use --replace to replace it, or --no-config to skip config creation.");
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const response = await createRepository(domain, name);
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
if (response.error instanceof ForbiddenRequestError) {
|
|
87
|
+
handleUnauthenticated();
|
|
88
|
+
} else {
|
|
89
|
+
console.error(`Failed to create repository: ${stringify(response.error)}`);
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Create or update config after successful repo creation
|
|
96
|
+
if (!noConfig) {
|
|
97
|
+
if (existingConfig.ok) {
|
|
98
|
+
const result = await updateConfig({ repositoryName: domain });
|
|
99
|
+
if (result.ok) {
|
|
100
|
+
console.info("Updated prismic.config.json");
|
|
101
|
+
} else {
|
|
102
|
+
console.warn("Could not update prismic.config.json: " + result.error.message);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
const result = await createConfig({ repositoryName: domain });
|
|
106
|
+
if (result.ok) {
|
|
107
|
+
console.info("Created prismic.config.json");
|
|
108
|
+
} else {
|
|
109
|
+
console.warn("Could not create prismic.config.json: " + result.error.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.info(`Repository created: ${domain}`);
|
|
115
|
+
console.info(`URL: ${await getRepoUrl(domain)}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function createRepository(domain: string, name = domain) {
|
|
119
|
+
const url = new URL("/app/dashboard/repositories", await readHost());
|
|
120
|
+
return await request(url, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
body: {
|
|
123
|
+
domain,
|
|
124
|
+
name,
|
|
125
|
+
framework: "next",
|
|
126
|
+
plan: "personal",
|
|
127
|
+
usageIntent: "Exploring Prismic's features for future projects.",
|
|
128
|
+
usageIntentIndex: 0,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function handleUnauthenticated() {
|
|
134
|
+
console.error("Not logged in. Run `prismic login` first.");
|
|
135
|
+
process.exitCode = 1;
|
|
136
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
|
|
4
|
+
import { isAuthenticated } from "./lib/auth";
|
|
5
|
+
import { safeGetRepositoryFromConfig } from "./lib/config";
|
|
6
|
+
import { stringify } from "./lib/json";
|
|
7
|
+
import { ForbiddenRequestError, request } from "./lib/request";
|
|
8
|
+
import { getRepoUrl } from "./lib/url";
|
|
9
|
+
|
|
10
|
+
const SyncStateSchema = v.object({
|
|
11
|
+
repository: v.object({
|
|
12
|
+
api_access: v.string(),
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const HELP = `
|
|
17
|
+
Get the Content API access level of a Prismic repository.
|
|
18
|
+
|
|
19
|
+
By default, this command reads the repository from prismic.config.json at the
|
|
20
|
+
project root.
|
|
21
|
+
|
|
22
|
+
USAGE
|
|
23
|
+
prismic repo get-access [flags]
|
|
24
|
+
|
|
25
|
+
FLAGS
|
|
26
|
+
-r, --repo string Repository domain
|
|
27
|
+
-h, --help Show help for command
|
|
28
|
+
|
|
29
|
+
LEARN MORE
|
|
30
|
+
Use \`prismic <command> <subcommand> --help\` for more information about a command.
|
|
31
|
+
`.trim();
|
|
32
|
+
|
|
33
|
+
export async function repoGetAccess(): Promise<void> {
|
|
34
|
+
const {
|
|
35
|
+
values: { help, repo = await safeGetRepositoryFromConfig() },
|
|
36
|
+
} = parseArgs({
|
|
37
|
+
args: process.argv.slice(4), // skip: node, script, "repo", "get-access"
|
|
38
|
+
options: {
|
|
39
|
+
repo: { type: "string", short: "r" },
|
|
40
|
+
help: { type: "boolean", short: "h" },
|
|
41
|
+
},
|
|
42
|
+
allowPositionals: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (help) {
|
|
46
|
+
console.info(HELP);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!repo) {
|
|
51
|
+
console.error("Missing prismic.config.json or --repo option");
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const authenticated = await isAuthenticated();
|
|
57
|
+
if (!authenticated) {
|
|
58
|
+
handleUnauthenticated();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const response = await getRepositoryAccess(repo);
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
if (response.error instanceof ForbiddenRequestError) {
|
|
65
|
+
handleUnauthenticated();
|
|
66
|
+
} else {
|
|
67
|
+
console.error(`Failed to get repository access: ${stringify(response.value)}`);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.info(response.value.repository.api_access);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function getRepositoryAccess(domain: string) {
|
|
77
|
+
const repoUrl = await getRepoUrl(domain);
|
|
78
|
+
const url = new URL("syncState", repoUrl);
|
|
79
|
+
|
|
80
|
+
return await request(url, { schema: SyncStateSchema });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleUnauthenticated() {
|
|
84
|
+
console.error("Not logged in. Run `prismic login` first.");
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
}
|