@camox/api-contract 0.71.0 → 0.74.1

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.
@@ -0,0 +1,17 @@
1
+ //#region src/claim-links.d.ts
2
+ /** URL search param carrying a Claim Link's token on the website it links to. */
3
+ export declare const CLAIM_LINK_SEARCH_PARAM = "camox-claim";
4
+ /**
5
+ * Where a Claim Link stands: `open` until it's claimed, revoked by an operator, or blocked, when a
6
+ * claim was refused because someone who isn't an operator joined its organization (ADR 0041).
7
+ */
8
+ export type ClaimLinkState = "open" | "claimed" | "revoked" | "blocked";
9
+ /**
10
+ * A Claim Link: the project's address, its Custom Domain if it has one, else its `camox.site`
11
+ * subdomain, with the link's token.
12
+ */
13
+ export declare function claimLinkUrl(project: {
14
+ slug: string;
15
+ customDomain: string | null;
16
+ }, token: string): string;
17
+ //#endregion
@@ -0,0 +1,17 @@
1
+ import { projectWebsiteUrl } from "./projects.js";
2
+
3
+ //#region src/claim-links.ts
4
+ /** URL search param carrying a Claim Link's token on the website it links to. */
5
+ const CLAIM_LINK_SEARCH_PARAM = "camox-claim";
6
+ /**
7
+ * A Claim Link: the project's address, its Custom Domain if it has one, else its `camox.site`
8
+ * subdomain, with the link's token.
9
+ */
10
+ function claimLinkUrl(project, token) {
11
+ const url = new URL("/", projectWebsiteUrl(project));
12
+ url.searchParams.set(CLAIM_LINK_SEARCH_PARAM, token);
13
+ return url.toString();
14
+ }
15
+
16
+ //#endregion
17
+ export { CLAIM_LINK_SEARCH_PARAM, claimLinkUrl };
package/dist/drafts.d.ts CHANGED
@@ -42,17 +42,17 @@ export declare const LOOK_PATH_PARAM = "path";
42
42
  export declare const LOOK_LOCALE_PARAM = "locale";
43
43
  /**
44
44
  * The look document's Block Definition mode: instead of a page, the block of the Block Definition
45
- * `LOOK_BLOCK_DEFINITION_PARAM` names, alone in the site's shell, with its default content and
46
- * Block Settings and the overrides `LOOK_CONTENT_PARAM` and `LOOK_SETTINGS_PARAM` give as JSON, in
47
- * the shape the content tools take.
45
+ * `LOOK_BLOCK_DEFINITION_PARAM` names, alone in the site's shell, with empty fields and the
46
+ * content and Block Settings `LOOK_CONTENT_PARAM` and `LOOK_SETTINGS_PARAM` give as JSON merged
47
+ * on, in the shape the content tools take.
48
48
  */
49
49
  export declare const LOOK_BLOCK_DEFINITION_PARAM = "blockDefinition";
50
50
  export declare const LOOK_CONTENT_PARAM = "content";
51
51
  export declare const LOOK_SETTINGS_PARAM = "settings";
52
52
  /**
53
53
  * The look document's Layout Definition mode: instead of a page, the curated Layout
54
- * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with their default
55
- * content and Block Settings and the overrides `LOOK_BLOCKS_PARAM` gives as JSON, by Block
54
+ * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with empty fields
55
+ * and the content and Block Settings `LOOK_BLOCKS_PARAM` gives as JSON merged on, by Block
56
56
  * Definition name (`{ navbar: { content, settings } }`), around a labelled placeholder where a
57
57
  * page's content goes, as tall as `LOOK_PLACEHOLDER_PARAM` says: one of `LOOK_PLACEHOLDER_HEIGHTS`,
58
58
  * the first without.
@@ -141,11 +141,6 @@ export type EndedDraft = {
141
141
  * so a page can render Live and say who published or deleted the draft.
142
142
  */
143
143
  export declare const DRAFT_ENDED_ERROR_CODE = "DRAFT_ENDED";
144
- /**
145
- * oRPC error code for a publish whose token no longer matches Live: Live changed since the
146
- * preview, so nothing was applied and the caller reviews the new preview.
147
- */
148
- export declare const CONFLICTS_CHANGED_ERROR = "CONFLICTS_CHANGED";
149
144
  /**
150
145
  * oRPC error code for a publish of a draft with code whose revision isn't the one the review
151
146
  * showed: the agent left a newer one, or the draft is already publishing. The caller reviews
@@ -154,42 +149,22 @@ export declare const CONFLICTS_CHANGED_ERROR = "CONFLICTS_CHANGED";
154
149
  export declare const REVISION_CHANGED_ERROR = "REVISION_CHANGED";
155
150
  /**
156
151
  * Why a publish with code went back to the review instead of landing: the agent left a revision
157
- * after the one the review showed, Live's conflicts changed, the draft got a blocker, Live's code
158
- * moved and the draft's merge of it clashed (the "doesn't merge with Live's" blocker, which
159
- * Camox's turn fixes), Live's code moved and the draft took too long to merge it, or the publish
160
- * failed on Camox's side.
152
+ * after the one the review showed, the draft got a blocker, Live's code moved and the draft's
153
+ * merge of it clashed (the "doesn't merge with Live's" blocker, which Camox's turn fixes), Live's
154
+ * code moved and the draft took too long to merge it, or the publish failed on Camox's side.
155
+ * `conflictsChanged` is only stored by publishes before ADR 0043, which aborted when Live changed.
161
156
  */
162
- export declare const PUBLISH_ABORT_REASONS: readonly ["revisionChanged", "conflictsChanged", "blocked", "codeDoesNotMerge", "liveCodeMoved", "failed"];
157
+ export declare const PUBLISH_ABORT_REASONS: readonly ["revisionChanged", "conflictsChanged", "blocked", "codeDoesNotBuild", "codeDoesNotMerge", "liveCodeMoved", "failed"];
163
158
  export type PublishAbortReason = (typeof PUBLISH_ABORT_REASONS)[number];
164
159
  /** oRPC error code for a publish refused because the draft still has blockers. */
165
160
  export declare const PUBLISH_BLOCKED_ERROR = "PUBLISH_BLOCKED";
166
- /**
167
- * oRPC error code for a publish refused because the draft still has pending translations: every
168
- * locale goes live together, and there is no publishing anyway (ADR 0033).
169
- */
170
- export declare const TRANSLATIONS_PENDING_ERROR = "TRANSLATIONS_PENDING";
171
- /**
172
- * oRPC error code for a publish refused because the draft has open findings, each to fix or for a
173
- * member to dismiss: there is no publishing past them (ADR 0033).
174
- */
175
- export declare const FINDINGS_OPEN_ERROR = "FINDINGS_OPEN";
176
- /**
177
- * oRPC error code for a publish refused because a patroller's check at publish hasn't read the
178
- * draft's latest changes yet: publishing waits for it (ADR 0033).
179
- */
180
- export declare const CHECKS_RUNNING_ERROR = "CHECKS_RUNNING";
181
161
  /**
182
162
  * Why the publish tool published nothing of a draft with changes, and returned a fresh preview to
183
- * review again, by the error code of each: Live changed since the preview, the draft has blockers,
184
- * translations are still running, the draft has open findings, a check at publish is still
185
- * running, or the draft's code changed since the preview.
163
+ * review again, by the error code of each: the draft has blockers, or the draft's code changed
164
+ * since the preview.
186
165
  */
187
166
  export declare const NOT_PUBLISHED_CODES: {
188
- readonly conflictsChanged: "CONFLICTS_CHANGED";
189
167
  readonly blocked: "PUBLISH_BLOCKED";
190
- readonly translating: "TRANSLATIONS_PENDING";
191
- readonly findings: "FINDINGS_OPEN";
192
- readonly checking: "CHECKS_RUNNING";
193
168
  readonly revisionChanged: "REVISION_CHANGED";
194
169
  };
195
170
  export type NotPublishedReason = keyof typeof NOT_PUBLISHED_CODES;
package/dist/drafts.js CHANGED
@@ -42,17 +42,17 @@ const LOOK_PATH_PARAM = "path";
42
42
  const LOOK_LOCALE_PARAM = "locale";
43
43
  /**
44
44
  * The look document's Block Definition mode: instead of a page, the block of the Block Definition
45
- * `LOOK_BLOCK_DEFINITION_PARAM` names, alone in the site's shell, with its default content and
46
- * Block Settings and the overrides `LOOK_CONTENT_PARAM` and `LOOK_SETTINGS_PARAM` give as JSON, in
47
- * the shape the content tools take.
45
+ * `LOOK_BLOCK_DEFINITION_PARAM` names, alone in the site's shell, with empty fields and the
46
+ * content and Block Settings `LOOK_CONTENT_PARAM` and `LOOK_SETTINGS_PARAM` give as JSON merged
47
+ * on, in the shape the content tools take.
48
48
  */
49
49
  const LOOK_BLOCK_DEFINITION_PARAM = "blockDefinition";
50
50
  const LOOK_CONTENT_PARAM = "content";
51
51
  const LOOK_SETTINGS_PARAM = "settings";
52
52
  /**
53
53
  * The look document's Layout Definition mode: instead of a page, the curated Layout
54
- * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with their default
55
- * content and Block Settings and the overrides `LOOK_BLOCKS_PARAM` gives as JSON, by Block
54
+ * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with empty fields
55
+ * and the content and Block Settings `LOOK_BLOCKS_PARAM` gives as JSON merged on, by Block
56
56
  * Definition name (`{ navbar: { content, settings } }`), around a labelled placeholder where a
57
57
  * page's content goes, as tall as `LOOK_PLACEHOLDER_PARAM` says: one of `LOOK_PLACEHOLDER_HEIGHTS`,
58
58
  * the first without.
@@ -91,11 +91,6 @@ const DRAFT_SEARCH_PARAM = "camox-draft";
91
91
  */
92
92
  const DRAFT_ENDED_ERROR_CODE = "DRAFT_ENDED";
93
93
  /**
94
- * oRPC error code for a publish whose token no longer matches Live: Live changed since the
95
- * preview, so nothing was applied and the caller reviews the new preview.
96
- */
97
- const CONFLICTS_CHANGED_ERROR = "CONFLICTS_CHANGED";
98
- /**
99
94
  * oRPC error code for a publish of a draft with code whose revision isn't the one the review
100
95
  * showed: the agent left a newer one, or the draft is already publishing. The caller reviews
101
96
  * the new preview.
@@ -103,15 +98,16 @@ const CONFLICTS_CHANGED_ERROR = "CONFLICTS_CHANGED";
103
98
  const REVISION_CHANGED_ERROR = "REVISION_CHANGED";
104
99
  /**
105
100
  * Why a publish with code went back to the review instead of landing: the agent left a revision
106
- * after the one the review showed, Live's conflicts changed, the draft got a blocker, Live's code
107
- * moved and the draft's merge of it clashed (the "doesn't merge with Live's" blocker, which
108
- * Camox's turn fixes), Live's code moved and the draft took too long to merge it, or the publish
109
- * failed on Camox's side.
101
+ * after the one the review showed, the draft got a blocker, Live's code moved and the draft's
102
+ * merge of it clashed (the "doesn't merge with Live's" blocker, which Camox's turn fixes), Live's
103
+ * code moved and the draft took too long to merge it, or the publish failed on Camox's side.
104
+ * `conflictsChanged` is only stored by publishes before ADR 0043, which aborted when Live changed.
110
105
  */
111
106
  const PUBLISH_ABORT_REASONS = [
112
107
  "revisionChanged",
113
108
  "conflictsChanged",
114
109
  "blocked",
110
+ "codeDoesNotBuild",
115
111
  "codeDoesNotMerge",
116
112
  "liveCodeMoved",
117
113
  "failed"
@@ -119,32 +115,12 @@ const PUBLISH_ABORT_REASONS = [
119
115
  /** oRPC error code for a publish refused because the draft still has blockers. */
120
116
  const PUBLISH_BLOCKED_ERROR = "PUBLISH_BLOCKED";
121
117
  /**
122
- * oRPC error code for a publish refused because the draft still has pending translations: every
123
- * locale goes live together, and there is no publishing anyway (ADR 0033).
124
- */
125
- const TRANSLATIONS_PENDING_ERROR = "TRANSLATIONS_PENDING";
126
- /**
127
- * oRPC error code for a publish refused because the draft has open findings, each to fix or for a
128
- * member to dismiss: there is no publishing past them (ADR 0033).
129
- */
130
- const FINDINGS_OPEN_ERROR = "FINDINGS_OPEN";
131
- /**
132
- * oRPC error code for a publish refused because a patroller's check at publish hasn't read the
133
- * draft's latest changes yet: publishing waits for it (ADR 0033).
134
- */
135
- const CHECKS_RUNNING_ERROR = "CHECKS_RUNNING";
136
- /**
137
118
  * Why the publish tool published nothing of a draft with changes, and returned a fresh preview to
138
- * review again, by the error code of each: Live changed since the preview, the draft has blockers,
139
- * translations are still running, the draft has open findings, a check at publish is still
140
- * running, or the draft's code changed since the preview.
119
+ * review again, by the error code of each: the draft has blockers, or the draft's code changed
120
+ * since the preview.
141
121
  */
142
122
  const NOT_PUBLISHED_CODES = {
143
- conflictsChanged: CONFLICTS_CHANGED_ERROR,
144
123
  blocked: PUBLISH_BLOCKED_ERROR,
145
- translating: TRANSLATIONS_PENDING_ERROR,
146
- findings: FINDINGS_OPEN_ERROR,
147
- checking: CHECKS_RUNNING_ERROR,
148
124
  revisionChanged: REVISION_CHANGED_ERROR
149
125
  };
150
126
  /**
@@ -159,85 +135,93 @@ const CAMOX_NAME = "Camox";
159
135
  * changes reach it only by publishing a draft, or through a release.
160
136
  */
161
137
  const LIVE_READ_ONLY_ERROR = "LIVE_READ_ONLY";
138
+ const BLOCK_WRITES = {
139
+ get: false,
140
+ getPageMarkdown: false,
141
+ getLayoutMarkdown: false,
142
+ getUsageCounts: false,
143
+ getPagesUsing: false,
144
+ getSyncedContent: false,
145
+ getUnplaced: false,
146
+ create: true,
147
+ updateContent: true,
148
+ updateSettings: true,
149
+ updatePosition: true,
150
+ delete: true,
151
+ deleteMany: true,
152
+ generateSummary: false,
153
+ duplicate: true
154
+ };
155
+ const COLLECTION_WRITES = {
156
+ list: false,
157
+ get: false,
158
+ listRecords: false,
159
+ getRecord: false,
160
+ getRecordUsageCount: false,
161
+ findRecordsByField: false,
162
+ listRecordPageValues: false,
163
+ recordMarkdown: false,
164
+ createRecord: true,
165
+ editRecord: true,
166
+ deleteRecord: true
167
+ };
168
+ const FILE_WRITES = {
169
+ list: false,
170
+ get: false,
171
+ getUsageCount: false,
172
+ setAlt: true,
173
+ setFilename: true,
174
+ delete: true,
175
+ deleteMany: true,
176
+ setAiMetadata: true,
177
+ generateMetadata: false
178
+ };
179
+ const PAGE_WRITES = {
180
+ getByPath: false,
181
+ getStructure: false,
182
+ getMarkdown: false,
183
+ list: false,
184
+ listBySlug: false,
185
+ get: false,
186
+ create: true,
187
+ update: true,
188
+ delete: true,
189
+ setSeo: true,
190
+ setLayout: true,
191
+ generateSeo: false
192
+ };
193
+ const ITEM_WRITES = {
194
+ get: false,
195
+ create: true,
196
+ updateContent: true,
197
+ updateSettings: true,
198
+ updatePosition: true,
199
+ duplicate: true,
200
+ generateSummary: false,
201
+ delete: true
202
+ };
203
+ const LOCALE_WRITES = {
204
+ list: false,
205
+ add: true,
206
+ remove: true,
207
+ setDefaultTag: false,
208
+ listMissing: false,
209
+ addBack: true
210
+ };
211
+ const SPECIALISTS_WRITES = {
212
+ get: false,
213
+ applyFix: true,
214
+ dismissFinding: false
215
+ };
162
216
  const CONTENT_WRITES = {
163
- blocks: {
164
- get: false,
165
- getPageMarkdown: false,
166
- getLayoutMarkdown: false,
167
- getUsageCounts: false,
168
- getPagesUsing: false,
169
- getSyncedContent: false,
170
- getUnplaced: false,
171
- create: true,
172
- updateContent: true,
173
- updateSettings: true,
174
- updatePosition: true,
175
- delete: true,
176
- deleteMany: true,
177
- generateSummary: false,
178
- duplicate: true
179
- },
180
- locales: {
181
- list: false,
182
- add: true,
183
- remove: true,
184
- setDefaultTag: false,
185
- listMissing: false,
186
- addBack: true
187
- },
188
- collectionDefinitions: {
189
- list: false,
190
- get: false,
191
- listRecords: false,
192
- getRecord: false,
193
- getRecordUsageCount: false,
194
- findRecordsByField: false,
195
- listRecordPageValues: false,
196
- recordMarkdown: false,
197
- createRecord: true,
198
- editRecord: true,
199
- deleteRecord: true
200
- },
201
- files: {
202
- list: false,
203
- get: false,
204
- getUsageCount: false,
205
- setAlt: true,
206
- setFilename: true,
207
- delete: true,
208
- deleteMany: true,
209
- setAiMetadata: true,
210
- generateMetadata: false
211
- },
212
- pages: {
213
- getByPath: false,
214
- getStructure: false,
215
- getMarkdown: false,
216
- list: false,
217
- listBySlug: false,
218
- get: false,
219
- create: true,
220
- update: true,
221
- delete: true,
222
- setSeo: true,
223
- setLayout: true,
224
- generateSeo: false
225
- },
226
- patrol: {
227
- get: false,
228
- applyFix: true,
229
- dismissFinding: false
230
- },
231
- repeatableItems: {
232
- get: false,
233
- create: true,
234
- updateContent: true,
235
- updateSettings: true,
236
- updatePosition: true,
237
- duplicate: true,
238
- generateSummary: false,
239
- delete: true
240
- }
217
+ blocks: BLOCK_WRITES,
218
+ locales: LOCALE_WRITES,
219
+ collectionDefinitions: COLLECTION_WRITES,
220
+ files: FILE_WRITES,
221
+ pages: PAGE_WRITES,
222
+ specialists: SPECIALISTS_WRITES,
223
+ patrol: SPECIALISTS_WRITES,
224
+ repeatableItems: ITEM_WRITES
241
225
  };
242
226
  /** Whether the procedure at `path` (`[router, procedure]`) writes content. */
243
227
  function isContentWrite(path) {
@@ -246,4 +230,4 @@ function isContentWrite(path) {
246
230
  }
247
231
 
248
232
  //#endregion
249
- export { ARTIFACT_FIELD_ATTRIBUTE, ARTIFACT_ITEM_ATTRIBUTE, CAMOX_ACTOR, CAMOX_NAME, CHECKS_RUNNING_ERROR, CONFLICTS_CHANGED_ERROR, DRAFT_CREATE_HEADER, DRAFT_ENDED_ERROR_CODE, DRAFT_HEADER, DRAFT_SEARCH_PARAM, FINDINGS_OPEN_ERROR, LIVE_READ_ONLY_ERROR, LOCALE_HEADER, LOOK_ARTIFACT_CAPTURE, LOOK_BLOCKS_PARAM, LOOK_BLOCK_DEFINITION_PARAM, LOOK_CONTENT_PARAM, LOOK_DOCUMENT_PATH, LOOK_LAYOUT_DEFINITION_PARAM, LOOK_LOCALE_PARAM, LOOK_PATH_PARAM, LOOK_PLACEHOLDER_HEIGHTS, LOOK_PLACEHOLDER_PARAM, LOOK_SETTINGS_PARAM, LOOK_TOKEN_COOKIE, LOOK_TOKEN_HEADER, NOT_PUBLISHED_CODES, PUBLISHING_DRAFT_HEADER, PUBLISHING_DRAFT_VAR, PUBLISH_ABORT_REASONS, PUBLISH_BLOCKED_ERROR, REVISION_CHANGED_ERROR, TRANSLATIONS_PENDING_ERROR, UNPLACED_BLOCK_ID, isContentWrite, lookArtifactSelector };
233
+ export { ARTIFACT_FIELD_ATTRIBUTE, ARTIFACT_ITEM_ATTRIBUTE, CAMOX_ACTOR, CAMOX_NAME, DRAFT_CREATE_HEADER, DRAFT_ENDED_ERROR_CODE, DRAFT_HEADER, DRAFT_SEARCH_PARAM, LIVE_READ_ONLY_ERROR, LOCALE_HEADER, LOOK_ARTIFACT_CAPTURE, LOOK_BLOCKS_PARAM, LOOK_BLOCK_DEFINITION_PARAM, LOOK_CONTENT_PARAM, LOOK_DOCUMENT_PATH, LOOK_LAYOUT_DEFINITION_PARAM, LOOK_LOCALE_PARAM, LOOK_PATH_PARAM, LOOK_PLACEHOLDER_HEIGHTS, LOOK_PLACEHOLDER_PARAM, LOOK_SETTINGS_PARAM, LOOK_TOKEN_COOKIE, LOOK_TOKEN_HEADER, NOT_PUBLISHED_CODES, PUBLISHING_DRAFT_HEADER, PUBLISHING_DRAFT_VAR, PUBLISH_ABORT_REASONS, PUBLISH_BLOCKED_ERROR, REVISION_CHANGED_ERROR, UNPLACED_BLOCK_ID, isContentWrite, lookArtifactSelector };
package/dist/index.d.ts CHANGED
@@ -17,6 +17,29 @@ export declare function artifactTitle(source: string): string;
17
17
  */
18
18
  export declare function artifactSourceHash(source: string): Promise<string>;
19
19
  //#endregion
20
+ //#region src/empty-values.d.ts
21
+ /**
22
+ * What a field holds before anyone writes it: the empty value of its kind. Fields declare no
23
+ * default, so content missing a field, or holding a value of the wrong kind, reads as this.
24
+ * Shared by the SDK (rendering, new repeatable items, authoring forms) and the API (creating
25
+ * content, conforming it on read), so both agree on what an unset field is.
26
+ */
27
+ /** A link that goes nowhere yet. */
28
+ export declare const EMPTY_LINK: {
29
+ readonly type: "external";
30
+ readonly text: "";
31
+ readonly href: "";
32
+ readonly newTab: false;
33
+ };
34
+ /**
35
+ * The empty value of a field, from its stored JSON Schema (`fieldType` and, for an enum, its
36
+ * options): `""` for text, a link to nowhere, `false`, an enum's first option, `null` for one
37
+ * asset or record, and `[]` for lists and repeaters. A fresh copy each call.
38
+ */
39
+ export declare function emptyFieldValue(fieldSchema: unknown): unknown;
40
+ /** The empty value of every field of a content or settings schema, by name. */
41
+ export declare function emptyFieldValues(schema: unknown): Record<string, unknown>;
42
+ //#endregion
20
43
  //#region src/record-templates.d.ts
21
44
  /**
22
45
  * A collection's `toTitle` and `toDescription` are record templates: text with `{{field}}` tokens
package/dist/index.js CHANGED
@@ -43,6 +43,51 @@ async function artifactSourceHash(source) {
43
43
  return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
44
44
  }
45
45
 
46
+ //#endregion
47
+ //#region src/empty-values.ts
48
+ /**
49
+ * What a field holds before anyone writes it: the empty value of its kind. Fields declare no
50
+ * default, so content missing a field, or holding a value of the wrong kind, reads as this.
51
+ * Shared by the SDK (rendering, new repeatable items, authoring forms) and the API (creating
52
+ * content, conforming it on read), so both agree on what an unset field is.
53
+ */
54
+ /** A link that goes nowhere yet. */
55
+ const EMPTY_LINK = {
56
+ type: "external",
57
+ text: "",
58
+ href: "",
59
+ newTab: false
60
+ };
61
+ function isObject(value) {
62
+ return typeof value === "object" && value !== null && !Array.isArray(value);
63
+ }
64
+ /**
65
+ * The empty value of a field, from its stored JSON Schema (`fieldType` and, for an enum, its
66
+ * options): `""` for text, a link to nowhere, `false`, an enum's first option, `null` for one
67
+ * asset or record, and `[]` for lists and repeaters. A fresh copy each call.
68
+ */
69
+ function emptyFieldValue(fieldSchema) {
70
+ const schema = isObject(fieldSchema) ? fieldSchema : {};
71
+ switch (schema.fieldType) {
72
+ case "Link": return { ...EMPTY_LINK };
73
+ case "Boolean": return false;
74
+ case "Enum": return (Array.isArray(schema.enum) ? schema.enum : []).find((option) => typeof option === "string") ?? "";
75
+ case "Image":
76
+ case "File":
77
+ case "Reference": return null;
78
+ case "ImageList":
79
+ case "FileList":
80
+ case "ReferenceList":
81
+ case "Repeater": return [];
82
+ default: return "";
83
+ }
84
+ }
85
+ /** The empty value of every field of a content or settings schema, by name. */
86
+ function emptyFieldValues(schema) {
87
+ const properties = isObject(schema) && isObject(schema.properties) ? schema.properties : {};
88
+ return Object.fromEntries(Object.entries(properties).map(([name, field]) => [name, emptyFieldValue(field)]));
89
+ }
90
+
46
91
  //#endregion
47
92
  //#region src/record-templates.ts
48
93
  /**
@@ -144,4 +189,4 @@ function isSettingCondition(value) {
144
189
  }
145
190
 
146
191
  //#endregion
147
- export { ARTICLE_TYPES, ICON_CATALOG_VERSION, PAGE_TEXT_LINK_PREFIX, QUERY_LIMIT_CAP, SYSTEM_ORDER_KEYS, UNTITLED_ARTIFACT, artifactSourceHash, artifactSourceTitle, artifactTitle, generateUuidv7, isPageUrlMarker, isQueryBacked, isSettingCondition, isSystemOrderKey, linkedRecordId, nonStringTemplateFields, projectIdSchema, recordLink, recordText, referenceQueryProblem, scopeIconSvgIds, templateFields, uuidv7JsonSchema, uuidv7Schema, validateIconValue };
192
+ export { ARTICLE_TYPES, EMPTY_LINK, ICON_CATALOG_VERSION, PAGE_TEXT_LINK_PREFIX, QUERY_LIMIT_CAP, SYSTEM_ORDER_KEYS, UNTITLED_ARTIFACT, artifactSourceHash, artifactSourceTitle, artifactTitle, emptyFieldValue, emptyFieldValues, generateUuidv7, isPageUrlMarker, isQueryBacked, isSettingCondition, isSystemOrderKey, linkedRecordId, nonStringTemplateFields, projectIdSchema, recordLink, recordText, referenceQueryProblem, scopeIconSvgIds, templateFields, uuidv7JsonSchema, uuidv7Schema, validateIconValue };
@@ -40,6 +40,9 @@ export declare const queryKeys: {
40
40
  list: ["camox", string, string];
41
41
  get: ["camox", string, string];
42
42
  };
43
+ specialists: {
44
+ get: ["camox", string];
45
+ };
43
46
  patrol: {
44
47
  get: ["camox", string];
45
48
  };
@@ -99,6 +99,7 @@ const queryKeys = {
99
99
  "get"
100
100
  ]
101
101
  },
102
+ specialists: { get: ["camox", "specialists"] },
102
103
  patrol: { get: ["camox", "patrol"] }
103
104
  };
104
105
  /** The last segment of a query key: what the query reads, Live or a draft (ADR 0011). */