@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,112 @@
1
+ import type { SharedSlice } from "@prismicio/types-internal/lib/customtypes";
2
+
3
+ import { readdir, readFile } from "node:fs/promises";
4
+ import * as v from "valibot";
5
+
6
+ import { exists, findUpward } from "./file";
7
+
8
+ export const SharedSliceSchema = v.object({
9
+ id: v.string(),
10
+ type: v.literal("SharedSlice"),
11
+ name: v.string(),
12
+ description: v.optional(v.string()),
13
+ variations: v.array(
14
+ v.object({
15
+ id: v.string(),
16
+ name: v.string(),
17
+ description: v.optional(v.string()),
18
+ docURL: v.optional(v.string()),
19
+ version: v.optional(v.string()),
20
+ imageUrl: v.optional(v.string()),
21
+ primary: v.optional(v.record(v.string(), v.unknown())),
22
+ items: v.optional(v.record(v.string(), v.unknown())),
23
+ }),
24
+ ),
25
+ });
26
+
27
+ type SliceModelResult =
28
+ | { ok: true; model: SharedSlice; modelPath: URL }
29
+ | { ok: false; error: string };
30
+
31
+ export async function findSliceModel(sliceId: string): Promise<SliceModelResult> {
32
+ const projectRoot = await findUpward("package.json");
33
+ if (!projectRoot) {
34
+ return { ok: false, error: "Could not find project root (no package.json found)" };
35
+ }
36
+
37
+ const slicesDirectory = await getSlicesDirectory();
38
+
39
+ // List all directories in slices folder
40
+ let entries: string[];
41
+ try {
42
+ entries = await readdir(slicesDirectory, { withFileTypes: false }) as unknown as string[];
43
+ } catch {
44
+ return { ok: false, error: `No slices directory found at ${slicesDirectory.href}` };
45
+ }
46
+
47
+ // Search for a slice with matching ID
48
+ for (const entry of entries) {
49
+ const modelPath = new URL(`${entry}/model.json`, slicesDirectory);
50
+ try {
51
+ const contents = await readFile(modelPath, "utf8");
52
+ const parsed = JSON.parse(contents);
53
+ if (parsed.id === sliceId) {
54
+ const result = v.safeParse(SharedSliceSchema, parsed);
55
+ if (!result.success) {
56
+ return { ok: false, error: `Invalid slice model at ${modelPath.href}` };
57
+ }
58
+ return { ok: true, model: result.output as SharedSlice, modelPath };
59
+ }
60
+ } catch {
61
+ // Skip directories without valid model.json
62
+ }
63
+ }
64
+
65
+ return { ok: false, error: `Slice not found: ${sliceId}\n\nCreate it first with: prismic slice create ${sliceId}` };
66
+ }
67
+
68
+ export async function getSlicesDirectory(): Promise<URL> {
69
+ const framework = await detectFramework();
70
+ const projectRoot = await findUpward("package.json");
71
+ if (!projectRoot) {
72
+ throw new Error("Could not find project root (no package.json found)");
73
+ }
74
+ const projectDir = new URL(".", projectRoot);
75
+
76
+ switch (framework) {
77
+ case "next": {
78
+ const hasSrcDir = await exists(new URL("src", projectDir));
79
+ if (hasSrcDir) return new URL("src/slices/", projectDir);
80
+ }
81
+ case "nuxt": {
82
+ const hasAppDir = await exists(new URL("app", projectDir));
83
+ if (hasAppDir) return new URL("app/slices/", projectDir);
84
+ }
85
+ case "sveltekit": {
86
+ return new URL("src/slices/", projectDir);
87
+ }
88
+ }
89
+ return new URL("slices/", projectDir);
90
+ }
91
+
92
+ const PackageJsonSchema = v.object({
93
+ dependencies: v.optional(v.record(v.string(), v.string())),
94
+ });
95
+
96
+ type Framework = "next" | "nuxt" | "sveltekit";
97
+
98
+ async function detectFramework(): Promise<Framework | undefined> {
99
+ const packageJsonPath = await findUpward("package.json");
100
+ if (!packageJsonPath) return;
101
+ try {
102
+ const contents = await readFile(packageJsonPath, "utf8");
103
+ const { dependencies = {} } = v.parse(PackageJsonSchema, JSON.parse(contents));
104
+ if ("next" in dependencies) return "next";
105
+ if ("nuxt" in dependencies) return "nuxt";
106
+ if ("@sveltejs/kit" in dependencies) return "sveltekit";
107
+ } catch {}
108
+ }
109
+
110
+ export function pascalCase(input: string): string {
111
+ return input.toLowerCase().replace(/(^|[-_\s]+)(.)?/g, (_, __, c) => c?.toUpperCase() ?? "");
112
+ }
package/src/lib/url.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { readHost } from "./auth";
2
+
3
+ export async function getRepoUrl(repo: string): Promise<URL> {
4
+ const host = await readHost();
5
+ host.hostname = `${repo}.${host.hostname}`;
6
+ return appendTrailingSlash(host);
7
+ }
8
+
9
+ export async function getInternalApiUrl(): Promise<URL> {
10
+ const host = await readHost();
11
+ host.hostname = `api.internal.${host.hostname}`;
12
+ return appendTrailingSlash(host);
13
+ }
14
+
15
+ export async function getUserServiceUrl(): Promise<URL> {
16
+ const host = await readHost();
17
+ host.hostname = `user-service.${host.hostname}`;
18
+ return appendTrailingSlash(host);
19
+ }
20
+
21
+ export function appendTrailingSlash(url: string | URL): URL {
22
+ const newURL = new URL(url);
23
+ if (!newURL.pathname.endsWith("/")) newURL.pathname += "/";
24
+ return newURL;
25
+ }
@@ -0,0 +1,116 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ import { isAuthenticated } from "./lib/auth";
4
+ import { safeGetRepositoryFromConfig } 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
+ Add a new locale to a Prismic repository.
11
+
12
+ By default, this command reads the repository from prismic.config.json at the
13
+ project root.
14
+
15
+ USAGE
16
+ prismic locale add <code> [flags]
17
+
18
+ ARGUMENTS
19
+ <code> Locale code (e.g. fr-fr, es-es)
20
+
21
+ FLAGS
22
+ -n, --name string Custom display name (creates custom locale)
23
+ -r, --repo string Repository domain
24
+ -h, --help Show help for command
25
+
26
+ LEARN MORE
27
+ Use \`prismic <command> <subcommand> --help\` for more information about a command.
28
+ `.trim();
29
+
30
+ export async function localeAdd(): Promise<void> {
31
+ const {
32
+ values: { help, name, repo = await safeGetRepositoryFromConfig() },
33
+ positionals: [code],
34
+ } = parseArgs({
35
+ args: process.argv.slice(4), // skip: node, script, "locale", "add"
36
+ options: {
37
+ name: { type: "string", short: "n" },
38
+ repo: { type: "string", short: "r" },
39
+ help: { type: "boolean", short: "h" },
40
+ },
41
+ allowPositionals: true,
42
+ });
43
+
44
+ if (help) {
45
+ console.info(HELP);
46
+ return;
47
+ }
48
+
49
+ if (!code) {
50
+ console.error("Missing required argument: <code>");
51
+ process.exitCode = 1;
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 authenticated = await isAuthenticated();
62
+ if (!authenticated) {
63
+ handleUnauthenticated();
64
+
65
+ return;
66
+ }
67
+
68
+ const response = name
69
+ ? await addCustomLocale(repo, code, name)
70
+ : await addStandardLocale(repo, code);
71
+ if (!response.ok) {
72
+ if (
73
+ typeof response.value === "string" &&
74
+ response.value.includes("already existing languages")
75
+ ) {
76
+ // Treat as success
77
+ return;
78
+ }
79
+
80
+ if (response.error instanceof ForbiddenRequestError) {
81
+ handleUnauthenticated();
82
+ } else {
83
+ console.error(`Failed to add locale: ${stringify(response.value)}`);
84
+ process.exitCode = 1;
85
+ }
86
+
87
+ return;
88
+ }
89
+
90
+ console.info(`Locale added: ${code}`);
91
+ }
92
+
93
+ async function addStandardLocale(repo: string, code: string) {
94
+ const url = new URL("/app/settings/multilanguages", await getRepoUrl(repo));
95
+ return await request(url, {
96
+ method: "POST",
97
+ body: { languages: [code] },
98
+ });
99
+ }
100
+
101
+ async function addCustomLocale(repo: string, code: string, name: string) {
102
+ const [langPart, regionPart] = code.split("-");
103
+ const url = new URL("/app/settings/multilanguages/custom", await getRepoUrl(repo));
104
+ return await request(url, {
105
+ method: "POST",
106
+ body: {
107
+ lang: { label: name, id: langPart || code },
108
+ region: { label: name, id: regionPart || langPart || code },
109
+ },
110
+ });
111
+ }
112
+
113
+ function handleUnauthenticated() {
114
+ console.error("Not logged in. Run `prismic login` first.");
115
+ process.exitCode = 1;
116
+ }
@@ -0,0 +1,107 @@
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, type ParsedRequestResponse, request } from "./lib/request";
8
+ import { getInternalApiUrl } from "./lib/url";
9
+
10
+ const HELP = `
11
+ List all locales in a Prismic repository.
12
+
13
+ By default, this command reads the repository from prismic.config.json at the
14
+ project root.
15
+
16
+ USAGE
17
+ prismic locale list [flags]
18
+
19
+ FLAGS
20
+ --json Output as JSON
21
+ -r, --repo string Repository domain
22
+ -h, --help Show help for command
23
+
24
+ LEARN MORE
25
+ Use \`prismic <command> <subcommand> --help\` for more information about a command.
26
+ `.trim();
27
+
28
+ export async function localeList(): Promise<void> {
29
+ const {
30
+ values: { help, repo = await safeGetRepositoryFromConfig(), json },
31
+ } = parseArgs({
32
+ args: process.argv.slice(4), // skip: node, script, "locale", "list"
33
+ options: {
34
+ json: { type: "boolean" },
35
+ repo: { type: "string", short: "r" },
36
+ help: { type: "boolean", short: "h" },
37
+ },
38
+ allowPositionals: false,
39
+ });
40
+
41
+ if (help) {
42
+ console.info(HELP);
43
+ return;
44
+ }
45
+
46
+ if (!repo) {
47
+ console.error("Missing prismic.config.json or --repo option");
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+
52
+ const authenticated = await isAuthenticated();
53
+ if (!authenticated) {
54
+ handleUnauthenticated();
55
+ return;
56
+ }
57
+
58
+ const response = await getLocales(repo);
59
+ if (!response.ok) {
60
+ if (response.error instanceof ForbiddenRequestError) {
61
+ handleUnauthenticated();
62
+ } else if (v.isValiError(response.error)) {
63
+ console.error(
64
+ `Failed to list locales: Invalid response: ${stringify(response.error.issues)}`,
65
+ );
66
+ process.exitCode = 1;
67
+ } else {
68
+ console.error(`Failed to list locales: ${stringify(response.value)}`);
69
+ process.exitCode = 1;
70
+ }
71
+ return;
72
+ }
73
+
74
+ const locales = response.value.results;
75
+ if (json) {
76
+ console.info(stringify(locales));
77
+ } else {
78
+ for (const locale of locales) {
79
+ const defaultLabel = locale.isMaster ? " (default)" : "";
80
+ console.info(`${locale.id} ${locale.label}${defaultLabel}`);
81
+ }
82
+ }
83
+ }
84
+
85
+ const LocaleSchema = v.object({
86
+ id: v.string(),
87
+ label: v.string(),
88
+ customName: v.nullable(v.string()),
89
+ isMaster: v.boolean(),
90
+ });
91
+ export type Locale = v.InferOutput<typeof LocaleSchema>;
92
+
93
+ const GetLocalesResponseSchema = v.object({
94
+ results: v.array(LocaleSchema),
95
+ });
96
+ type GetLocalesResponse = v.InferOutput<typeof GetLocalesResponseSchema>;
97
+
98
+ export async function getLocales(repo: string): Promise<ParsedRequestResponse<GetLocalesResponse>> {
99
+ const url = new URL("/locale/repository/locales", await getInternalApiUrl());
100
+ url.searchParams.set("repository", repo);
101
+ return await request(url, { schema: GetLocalesResponseSchema });
102
+ }
103
+
104
+ function handleUnauthenticated() {
105
+ console.error("Not logged in. Run `prismic login` first.");
106
+ process.exitCode = 1;
107
+ }
@@ -0,0 +1,88 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ import { isAuthenticated } from "./lib/auth";
4
+ import { safeGetRepositoryFromConfig } from "./lib/config";
5
+ import { stringify } from "./lib/json";
6
+ import { ForbiddenRequestError, request } from "./lib/request";
7
+ import { getInternalApiUrl } from "./lib/url";
8
+
9
+ const HELP = `
10
+ Remove a locale from a Prismic repository.
11
+
12
+ By default, this command reads the repository from prismic.config.json at the
13
+ project root.
14
+
15
+ USAGE
16
+ prismic locale remove <code> [flags]
17
+
18
+ ARGUMENTS
19
+ <code> Locale code (e.g. en-us, fr-fr)
20
+
21
+ FLAGS
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
+ export async function localeRemove(): Promise<void> {
30
+ const {
31
+ values: { repo = await safeGetRepositoryFromConfig(), help },
32
+ positionals: [code],
33
+ } = parseArgs({
34
+ args: process.argv.slice(4), // skip: node, script, "locale", "remove"
35
+ options: {
36
+ repo: { type: "string", short: "r" },
37
+ help: { type: "boolean", short: "h" },
38
+ },
39
+ allowPositionals: true,
40
+ });
41
+
42
+ if (help) {
43
+ console.info(HELP);
44
+ return;
45
+ }
46
+
47
+ if (!code) {
48
+ console.error("Missing required argument: <code>");
49
+ process.exitCode = 1;
50
+ return;
51
+ }
52
+
53
+ if (!repo) {
54
+ console.error("Missing prismic.config.json or --repo option");
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+
59
+ const authenticated = await isAuthenticated();
60
+ if (!authenticated) {
61
+ handleUnauthenticated();
62
+ return;
63
+ }
64
+
65
+ const response = await removeLocale(repo, code);
66
+ if (!response.ok) {
67
+ if (response.error instanceof ForbiddenRequestError) {
68
+ handleUnauthenticated();
69
+ } else {
70
+ console.error(`Failed to remove locale: ${stringify(response.value)}`);
71
+ process.exitCode = 1;
72
+ }
73
+ return;
74
+ }
75
+
76
+ console.info(`Removed locale: ${code}`);
77
+ }
78
+
79
+ async function removeLocale(repository: string, code: string) {
80
+ const url = new URL(`/locale/repository/locales/${code}`, await getInternalApiUrl());
81
+ url.searchParams.set("repository", repository);
82
+ return await request(url, { method: "DELETE" });
83
+ }
84
+
85
+ function handleUnauthenticated() {
86
+ console.error("Not logged in. Run `prismic login` first.");
87
+ process.exitCode = 1;
88
+ }
@@ -0,0 +1,131 @@
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 { getInternalApiUrl } from "./lib/url";
9
+ import { type Locale, getLocales } from "./locale-list";
10
+
11
+ const HELP = `
12
+ Set the default locale for 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 locale set-default <code> [flags]
19
+
20
+ ARGUMENTS
21
+ <code> Locale code (e.g. en-us, fr-fr)
22
+
23
+ FLAGS
24
+ -r, --repo string Repository domain
25
+ -h, --help Show help for command
26
+
27
+ LEARN MORE
28
+ Use \`prismic <command> <subcommand> --help\` for more information about a command.
29
+ `.trim();
30
+
31
+ export async function localeSetDefault(): Promise<void> {
32
+ const {
33
+ values: { help, repo = await safeGetRepositoryFromConfig() },
34
+ positionals: [code],
35
+ } = parseArgs({
36
+ args: process.argv.slice(4), // skip: node, script, "locale", "set-default"
37
+ options: {
38
+ repo: { type: "string", short: "r" },
39
+ help: { type: "boolean", short: "h" },
40
+ },
41
+ allowPositionals: true,
42
+ });
43
+
44
+ if (help) {
45
+ console.info(HELP);
46
+ return;
47
+ }
48
+
49
+ if (!code) {
50
+ console.error("Missing required argument: <code>");
51
+ process.exitCode = 1;
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 authenticated = await isAuthenticated();
62
+ if (!authenticated) {
63
+ handleUnauthenticated();
64
+ return;
65
+ }
66
+
67
+ const localesResponse = await getLocales(repo);
68
+ if (!localesResponse.ok) {
69
+ if (localesResponse.error instanceof ForbiddenRequestError) {
70
+ handleUnauthenticated();
71
+ } else if (v.isValiError(localesResponse.error)) {
72
+ console.error(
73
+ `Failed to set default locale: Invalid response: ${stringify(localesResponse.error.issues)}`,
74
+ );
75
+ process.exitCode = 1;
76
+ } else {
77
+ console.error(`Failed to set default locale: ${stringify(localesResponse.value)}`);
78
+ process.exitCode = 1;
79
+ }
80
+
81
+ return;
82
+ }
83
+
84
+ const locales = localesResponse.value.results;
85
+ const locale = locales.find((l) => l.id === code);
86
+ if (!locale) {
87
+ console.error(
88
+ `Locale "${code}" not found in repository. Available locales: ${locales.map((l) => l.id).join(", ")}`,
89
+ );
90
+ process.exitCode = 1;
91
+ return;
92
+ }
93
+
94
+ if (locale.isMaster) {
95
+ console.error(`Locale "${code}" is already the default.`);
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+
100
+ const response = await setDefaultLocale(repo, locale);
101
+ if (!response.ok) {
102
+ if (response.error instanceof ForbiddenRequestError) {
103
+ handleUnauthenticated();
104
+ } else {
105
+ console.error(`Failed to set default locale: ${stringify(response.value)}`);
106
+ process.exitCode = 1;
107
+ }
108
+ return;
109
+ }
110
+
111
+ console.info(`Default locale set: ${code}`);
112
+ }
113
+
114
+ async function setDefaultLocale(repo: string, locale: Locale) {
115
+ const url = new URL("/locale/repository/locales", await getInternalApiUrl());
116
+ url.searchParams.set("repository", repo);
117
+ return await request(url, {
118
+ method: "POST",
119
+ body: {
120
+ id: locale.id,
121
+ label: locale.label,
122
+ customName: locale.customName,
123
+ isMaster: true,
124
+ },
125
+ });
126
+ }
127
+
128
+ function handleUnauthenticated() {
129
+ console.error("Not logged in. Run `prismic login` first.");
130
+ process.exitCode = 1;
131
+ }
package/src/locale.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { parseArgs } from "node:util";
2
+
3
+ import { localeAdd } from "./locale-add";
4
+ import { localeList } from "./locale-list";
5
+ import { localeRemove } from "./locale-remove";
6
+ import { localeSetDefault } from "./locale-set-default";
7
+
8
+ const HELP = `
9
+ Manage locales in a Prismic repository.
10
+
11
+ USAGE
12
+ prismic locale <command> [flags]
13
+
14
+ COMMANDS
15
+ add Add a locale to a repository
16
+ list List locales in a repository
17
+ remove Remove a locale from a repository
18
+ set-default Set the default locale for a repository
19
+
20
+ FLAGS
21
+ -h, --help Show help for command
22
+
23
+ LEARN MORE
24
+ Use \`prismic locale <command> --help\` for more information about a command.
25
+ `.trim();
26
+
27
+ export async function locale(): Promise<void> {
28
+ const {
29
+ positionals: [subcommand],
30
+ } = parseArgs({
31
+ args: process.argv.slice(3), // skip: node, script, "locale"
32
+ options: {
33
+ help: { type: "boolean", short: "h" },
34
+ },
35
+ allowPositionals: true,
36
+ strict: false,
37
+ });
38
+
39
+ switch (subcommand) {
40
+ case "add":
41
+ await localeAdd();
42
+ break;
43
+ case "list":
44
+ await localeList();
45
+ break;
46
+ case "remove":
47
+ await localeRemove();
48
+ break;
49
+ case "set-default":
50
+ await localeSetDefault();
51
+ break;
52
+ default: {
53
+ if (subcommand) {
54
+ console.error(`Unknown locale subcommand: ${subcommand}\n`);
55
+ process.exitCode = 1;
56
+ }
57
+ console.info(HELP);
58
+ }
59
+ }
60
+ }