@spacefast/common 0.2.1 → 0.4.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.
- package/dist/agents/marketplace-prompts.d.ts +1 -0
- package/dist/agents/marketplace-prompts.js +6 -0
- package/dist/agents/registry/core.d.ts +4 -12
- package/dist/agents/registry/core.js +7 -28
- package/dist/config/domains.d.ts +10 -8
- package/dist/config/domains.js +4 -7
- package/dist/contracts/access-core.d.ts +135 -0
- package/dist/contracts/access-core.js +242 -0
- package/dist/contracts/access-profiles.d.ts +8 -8
- package/dist/contracts/access-profiles.js +8 -8
- package/dist/contracts/access.d.ts +14 -103
- package/dist/contracts/access.js +10 -210
- package/dist/contracts/activity.d.ts +54 -55
- package/dist/contracts/activity.js +38 -9
- package/dist/contracts/api-keys.d.ts +69 -26
- package/dist/contracts/api-keys.js +29 -2
- package/dist/contracts/application-journal.d.ts +197 -4
- package/dist/contracts/application-journal.js +97 -16
- package/dist/contracts/auth.d.ts +20 -0
- package/dist/contracts/auth.js +18 -0
- package/dist/contracts/beta.d.ts +13 -0
- package/dist/contracts/beta.js +28 -0
- package/dist/contracts/billing.d.ts +2 -2
- package/dist/contracts/bootstrap.d.ts +23 -6
- package/dist/contracts/builds.d.ts +24 -4
- package/dist/contracts/builds.js +16 -4
- package/dist/contracts/channels.d.ts +43 -0
- package/dist/contracts/channels.js +22 -6
- package/dist/contracts/comments.d.ts +49 -0
- package/dist/contracts/comments.js +61 -2
- package/dist/contracts/commerce.d.ts +19 -13
- package/dist/contracts/connector-access.d.ts +28 -0
- package/dist/contracts/connector-access.js +31 -0
- package/dist/contracts/connectors.d.ts +2382 -0
- package/dist/contracts/connectors.js +941 -0
- package/dist/contracts/content-contract-verification.d.ts +112 -58
- package/dist/contracts/content-contract-verification.js +42 -54
- package/dist/contracts/content-model-emit.d.ts +97 -0
- package/dist/contracts/content-model-emit.js +306 -0
- package/dist/contracts/content-model-sdk.d.ts +101 -0
- package/dist/contracts/content-model-sdk.js +232 -0
- package/dist/contracts/{content-program.d.ts → content-model.d.ts} +338 -82
- package/dist/contracts/{content-program.js → content-model.js} +291 -63
- package/dist/contracts/content-platform-fixture.d.ts +380 -110
- package/dist/contracts/content-platform-fixture.js +8 -8
- package/dist/contracts/content-sync.d.ts +197 -74
- package/dist/contracts/content-sync.js +98 -37
- package/dist/contracts/content.d.ts +30 -422
- package/dist/contracts/content.js +34 -294
- package/dist/contracts/dashboard-prefs.d.ts +10 -3
- package/dist/contracts/dashboard-prefs.js +6 -3
- package/dist/contracts/data-location.d.ts +9 -0
- package/dist/contracts/data-location.js +7 -0
- package/dist/contracts/deployment-comparison.d.ts +67 -0
- package/dist/contracts/deployment-comparison.js +68 -0
- package/dist/contracts/device-auth.d.ts +55 -11
- package/dist/contracts/domains.d.ts +40 -16
- package/dist/contracts/domains.js +23 -0
- package/dist/contracts/enums.d.ts +1 -10
- package/dist/contracts/enums.js +0 -15
- package/dist/contracts/error-code-meta.d.ts +388 -20
- package/dist/contracts/error-code-meta.js +201 -5
- package/dist/contracts/error-codes.d.ts +17 -4
- package/dist/contracts/error-codes.js +109 -10
- package/dist/contracts/events.d.ts +5 -0
- package/dist/contracts/events.js +12 -3
- package/dist/contracts/execution.d.ts +5 -1
- package/dist/contracts/execution.js +4 -0
- package/dist/contracts/feature-lifecycle.d.ts +53 -20
- package/dist/contracts/feature-lifecycle.js +65 -23
- package/dist/contracts/features.d.ts +57 -31
- package/dist/contracts/features.js +0 -1
- package/dist/contracts/feedback.d.ts +124 -0
- package/dist/contracts/feedback.js +64 -0
- package/dist/contracts/fixtures/content-model-sdk-v1.generated.d.ts +129 -0
- package/dist/contracts/fixtures/content-model-sdk-v1.generated.js +48 -0
- package/dist/contracts/fixtures/content-platform-v1.json +101 -49
- package/dist/contracts/frame-session.d.ts +60 -10
- package/dist/contracts/frame-session.js +52 -12
- package/dist/contracts/functions.d.ts +28 -2
- package/dist/contracts/functions.js +25 -2
- package/dist/contracts/grant-copy.d.ts +2 -0
- package/dist/contracts/grant-copy.js +2 -0
- package/dist/contracts/grants-core.d.ts +95 -0
- package/dist/contracts/grants-core.js +435 -0
- package/dist/contracts/grants.d.ts +13 -84
- package/dist/contracts/grants.js +21 -369
- package/dist/contracts/ids-core.d.ts +11 -0
- package/dist/contracts/ids-core.js +14 -0
- package/dist/contracts/ids.d.ts +7 -4
- package/dist/contracts/ids.js +12 -4
- package/dist/contracts/internal.d.ts +18 -0
- package/dist/contracts/internal.js +19 -0
- package/dist/contracts/mcp.d.ts +8 -18
- package/dist/contracts/mcp.js +2 -23
- package/dist/contracts/notifications.d.ts +4 -1
- package/dist/contracts/notifications.js +1 -0
- package/dist/contracts/oauth-resources.d.ts +55 -9
- package/dist/contracts/oauth-resources.js +64 -1
- package/dist/contracts/oauth-scope-actions.d.ts +27 -3
- package/dist/contracts/oauth-scope-actions.js +18 -8
- package/dist/contracts/operations.d.ts +1 -1
- package/dist/contracts/partner-account.d.ts +629 -0
- package/dist/contracts/partner-account.js +18 -0
- package/dist/contracts/principal-assertion.d.ts +74 -1
- package/dist/contracts/principal-assertion.js +67 -3
- package/dist/contracts/publish-archive.d.ts +22 -1
- package/dist/contracts/push-new.d.ts +3 -3
- package/dist/contracts/push-new.js +1 -1
- package/dist/contracts/quotas.js +2 -1
- package/dist/contracts/repository-connections.d.ts +10 -6
- package/dist/contracts/repository-connections.js +15 -0
- package/dist/contracts/resources.d.ts +6 -5
- package/dist/contracts/resources.js +7 -0
- package/dist/contracts/route-inventory.d.ts +74 -19
- package/dist/contracts/route-inventory.js +20 -6
- package/dist/contracts/runtime-api.d.ts +415 -4
- package/dist/contracts/runtime-api.js +60 -15
- package/dist/contracts/runtime-app.d.ts +6 -1
- package/dist/contracts/runtime-components.d.ts +646 -18
- package/dist/contracts/runtime-components.js +304 -188
- package/dist/contracts/runtime-services.d.ts +15 -0
- package/dist/contracts/runtime-services.js +25 -0
- package/dist/contracts/runtime-storage.d.ts +32 -0
- package/dist/contracts/runtime-storage.js +37 -0
- package/dist/contracts/source-workspaces.d.ts +511 -0
- package/dist/contracts/source-workspaces.js +276 -0
- package/dist/contracts/space-config-spacefast.d.ts +394 -0
- package/dist/contracts/space-config-spacefast.js +70 -0
- package/dist/contracts/space-config.d.ts +177 -90
- package/dist/contracts/space-config.js +77 -92
- package/dist/contracts/spaces.d.ts +132 -24
- package/dist/contracts/spaces.js +77 -13
- package/dist/contracts/superadmin-connectors.d.ts +371 -0
- package/dist/contracts/superadmin-connectors.js +61 -0
- package/dist/contracts/superadmin-emails.d.ts +1 -1
- package/dist/contracts/superadmin-emails.js +1 -1
- package/dist/contracts/superadmin-entities.d.ts +105 -0
- package/dist/contracts/superadmin-entities.js +64 -0
- package/dist/contracts/superadmin-runtime.d.ts +35 -116
- package/dist/contracts/superadmin-runtime.js +13 -69
- package/dist/contracts/superadmin-search.d.ts +12 -14
- package/dist/contracts/superadmin-search.js +2 -2
- package/dist/contracts/superadmin-spaces.d.ts +15 -72
- package/dist/contracts/superadmin-spaces.js +2 -7
- package/dist/contracts/superadmin-teams.d.ts +12 -0
- package/dist/contracts/superadmin-teams.js +7 -1
- package/dist/contracts/superadmin-tenants.d.ts +44 -16
- package/dist/contracts/superadmin.d.ts +2 -2
- package/dist/contracts/tags.d.ts +12 -12
- package/dist/contracts/teams.d.ts +29 -1
- package/dist/contracts/teams.js +24 -2
- package/dist/contracts/tenants.d.ts +98 -0
- package/dist/contracts/tenants.js +20 -0
- package/dist/contracts/transfers.d.ts +92 -0
- package/dist/contracts/transfers.js +37 -0
- package/dist/contracts/usage.d.ts +9 -1
- package/dist/contracts/usage.js +2 -0
- package/dist/contracts/visual-review.d.ts +448 -0
- package/dist/contracts/visual-review.js +156 -0
- package/dist/contracts/wordpress-php-toolkit.json +9 -0
- package/dist/contracts/work-mode.d.ts +24 -0
- package/dist/contracts/work-mode.js +17 -0
- package/dist/contracts/wp-cli.d.ts +66 -0
- package/dist/contracts/wp-cli.js +68 -0
- package/dist/contracts/zero-catalog.d.ts +29 -0
- package/dist/contracts/zero-catalog.js +55 -0
- package/dist/contracts/zero-platform-abilities.generated.d.ts +31 -0
- package/dist/contracts/zero-platform-abilities.generated.js +373 -0
- package/dist/contracts/zero-spacefast.d.ts +444 -0
- package/dist/contracts/zero-spacefast.js +149 -0
- package/dist/contracts/zero-types.d.ts +47 -0
- package/dist/contracts/zero-types.js +263 -0
- package/dist/contracts/zero.d.ts +781 -14
- package/dist/contracts/zero.js +204 -10
- package/dist/dashboard-paths/index.d.ts +2 -2
- package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -2
- package/dist/dashboard-paths/route-reservations.generated.js +5 -0
- package/dist/docs/agent-prose.d.ts +1 -1
- package/dist/docs/agent-prose.js +15 -18
- package/dist/docs/agent-setup.d.ts +1 -1
- package/dist/docs/agent-setup.js +5 -13
- package/dist/docs/catalog.d.ts +64 -28
- package/dist/docs/catalog.js +155 -28
- package/dist/docs/error-docs.d.ts +365 -17
- package/dist/docs/error-docs.js +369 -21
- package/dist/docs/reference-urls.js +1 -1
- package/dist/docs/skills.d.ts +84 -84
- package/dist/docs/skills.js +66 -47
- package/dist/docs/start-prompts.d.ts +14 -0
- package/dist/docs/start-prompts.js +20 -0
- package/dist/utils/account-labels.d.ts +7 -0
- package/dist/utils/account-labels.js +15 -0
- package/dist/utils/auth.d.ts +1 -0
- package/dist/utils/auth.js +1 -0
- package/dist/utils/credential-policy.d.ts +22 -1
- package/dist/utils/credential-policy.js +55 -6
- package/dist/utils/local-space-state.d.ts +7 -0
- package/dist/utils/local-space-state.js +7 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.js +144 -0
- package/dist/utils/page-routes.d.ts +9 -0
- package/dist/utils/page-routes.js +82 -0
- package/dist/utils/pages.d.ts +2 -0
- package/dist/utils/pages.js +7 -0
- package/dist/utils/publish-form-data.d.ts +2 -0
- package/dist/utils/publish-form-data.js +2 -0
- package/dist/utils/publish-policy-spacefast.d.ts +13 -0
- package/dist/utils/publish-policy-spacefast.js +117 -0
- package/dist/utils/publish-policy.d.ts +22 -16
- package/dist/utils/publish-policy.fixtures.json +15 -0
- package/dist/utils/publish-policy.js +62 -139
- package/dist/utils/query-client.js +9 -1
- package/dist/utils/runtime-upload.d.ts +4 -0
- package/dist/utils/runtime-upload.js +5 -0
- package/dist/utils/space-config.d.ts +2 -3
- package/dist/utils/space-config.js +1 -1
- package/dist/utils/uploaded-source.d.ts +4 -0
- package/dist/utils/uploaded-source.js +12 -0
- package/dist/visual-review.d.ts +4 -0
- package/dist/visual-review.js +208 -0
- package/dist/vocabulary.d.ts +38 -0
- package/dist/vocabulary.js +55 -0
- package/package.json +7 -2
- package/dist/agents/registry/deeplink-probes.d.ts +0 -23
- package/dist/agents/registry/deeplink-probes.js +0 -18
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { appRoutePathSchema, executionSha256Schema, httpMethodSchema } from "./execution.js";
|
|
3
|
-
import { grantCapabilitySchema } from "./grants.js";
|
|
4
|
-
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
const
|
|
3
|
+
import { grantCapabilitySchema } from "./grants-core.js";
|
|
4
|
+
import { ZERO_PLATFORM_ABILITIES } from "./zero-platform-abilities.generated.js";
|
|
5
|
+
export const CONTENT_MODEL_FORMAT = "spacefast.zero-wordpress";
|
|
6
|
+
export const CONTENT_MODEL_VERSION = 1;
|
|
7
|
+
export const contentModelSha256Schema = executionSha256Schema.brand();
|
|
8
|
+
/**
|
|
9
|
+
* The one spelling of a content model identifier.
|
|
10
|
+
*
|
|
11
|
+
* Exported because every "author → content model" compiler needs to normalize
|
|
12
|
+
* someone else's identifiers against it, and a hand-copied regex that drifts
|
|
13
|
+
* from this one produces ids the release then refuses.
|
|
14
|
+
*/
|
|
15
|
+
export const contentStableIdSchema = z
|
|
8
16
|
.string()
|
|
9
17
|
.min(1)
|
|
10
18
|
.max(160)
|
|
11
19
|
.regex(/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/);
|
|
20
|
+
const stableIdSchema = contentStableIdSchema;
|
|
12
21
|
export const contentResourceIdSchema = stableIdSchema.brand();
|
|
13
22
|
export const contentFieldIdSchema = stableIdSchema.brand();
|
|
14
23
|
export const contentTableIdSchema = stableIdSchema.brand();
|
|
@@ -16,6 +25,42 @@ export const contentAbilityIdSchema = stableIdSchema.brand();
|
|
|
16
25
|
export const contentSchemaIdSchema = stableIdSchema.brand();
|
|
17
26
|
export const contentComponentIdSchema = stableIdSchema.brand();
|
|
18
27
|
export const contentSyncBindingIdSchema = stableIdSchema.brand();
|
|
28
|
+
/**
|
|
29
|
+
* The name WordPress publishes an Ability under, derived from its content model id.
|
|
30
|
+
*
|
|
31
|
+
* Content model ids stay the vocabulary everywhere Spacefast addresses an Ability —
|
|
32
|
+
* routes, SDK descriptors, executables, decisions, receipts, invalidations.
|
|
33
|
+
* WordPress is the exception: its Abilities registry admits
|
|
34
|
+
* `^[a-z0-9-]+/[a-z0-9-]+$`, one namespace and one slug, so a dotted id
|
|
35
|
+
* flattens onto a single Zero-branded slug and `content.projects.list` is
|
|
36
|
+
* published as `zero/content-projects-list`.
|
|
37
|
+
*
|
|
38
|
+
* `spacefast_content_model_ability_name` in runtime/engine/wordpress/content-model-kernel.php
|
|
39
|
+
* is the same derivation in PHP and the two must change together. Because
|
|
40
|
+
* flattening is many-to-one (`a.b` and `a_b` collide),
|
|
41
|
+
* `contentModelReleaseV1Schema` rejects a content model whose abilities would share
|
|
42
|
+
* a name.
|
|
43
|
+
*/
|
|
44
|
+
export function contentAbilityWordPressName(id) {
|
|
45
|
+
return `zero/${id.replaceAll(".", "-").replaceAll("_", "-")}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The TypeScript identifier the generated content model SDK gives an Ability.
|
|
49
|
+
*
|
|
50
|
+
* This flattening is coarser than `contentAbilityWordPressName`: it drops every
|
|
51
|
+
* `.`/`_`/`-` separator and PascalCases the parts, so two ids that keep distinct
|
|
52
|
+
* WordPress names can still land on the same identifier (`posts.v2` and
|
|
53
|
+
* `posts.v.2` both become `PostsV2`, because a digit-led part has no letter to
|
|
54
|
+
* capitalize). `contentModelReleaseV1Schema` rejects that collision so the
|
|
55
|
+
* generated module never emits a duplicate `export type`.
|
|
56
|
+
*/
|
|
57
|
+
export function contentAbilityTypeName(id) {
|
|
58
|
+
return id
|
|
59
|
+
.split(/[^a-z0-9]+/)
|
|
60
|
+
.filter((part) => part.length > 0)
|
|
61
|
+
.map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
|
|
62
|
+
.join("");
|
|
63
|
+
}
|
|
19
64
|
export const contentSyncSourcePathSchema = z
|
|
20
65
|
.string()
|
|
21
66
|
.min(1)
|
|
@@ -27,7 +72,7 @@ export const contentSyncSourcePathSchema = z
|
|
|
27
72
|
}), "source paths cannot contain controls or backslashes")
|
|
28
73
|
.refine((source) => !source.startsWith("/") &&
|
|
29
74
|
source.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".."), "source paths must be normalized relative paths without traversal");
|
|
30
|
-
export const
|
|
75
|
+
export const contentModelJsonValueSchema = z.json();
|
|
31
76
|
export const contentResourceRefSchema = z
|
|
32
77
|
.string()
|
|
33
78
|
.regex(/^(?:content|table)\.[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/)
|
|
@@ -35,14 +80,14 @@ export const contentResourceRefSchema = z
|
|
|
35
80
|
export const contentSchemaRefSchema = z
|
|
36
81
|
.object({
|
|
37
82
|
id: contentSchemaIdSchema,
|
|
38
|
-
sha256:
|
|
83
|
+
sha256: contentModelSha256Schema,
|
|
39
84
|
})
|
|
40
85
|
.strict();
|
|
41
86
|
export const contentSchemaDefinitionSchema = z
|
|
42
87
|
.object({
|
|
43
88
|
id: contentSchemaIdSchema,
|
|
44
|
-
sha256:
|
|
45
|
-
schema:
|
|
89
|
+
sha256: contentModelSha256Schema,
|
|
90
|
+
schema: contentModelJsonValueSchema,
|
|
46
91
|
})
|
|
47
92
|
.strict();
|
|
48
93
|
const fieldValueSchema = z.discriminatedUnion("kind", [
|
|
@@ -78,7 +123,7 @@ const fieldValueSchema = z.discriminatedUnion("kind", [
|
|
|
78
123
|
})
|
|
79
124
|
.strict(),
|
|
80
125
|
]);
|
|
81
|
-
export const
|
|
126
|
+
export const contentModelFieldSchema = z
|
|
82
127
|
.object({
|
|
83
128
|
id: contentFieldIdSchema,
|
|
84
129
|
name: stableIdSchema,
|
|
@@ -93,13 +138,13 @@ export const wordpressContentResourceSchema = z
|
|
|
93
138
|
name: stableIdSchema,
|
|
94
139
|
label: z.string().trim().min(1).max(160),
|
|
95
140
|
kind: z.enum(["posts", "pages", "media", "collection"]),
|
|
96
|
-
fields: z.array(
|
|
141
|
+
fields: z.array(contentModelFieldSchema).max(100),
|
|
97
142
|
publicRead: z.boolean(),
|
|
98
143
|
})
|
|
99
144
|
.strict();
|
|
100
|
-
export const
|
|
145
|
+
export const wordpressContentModelIrV1Schema = z
|
|
101
146
|
.object({
|
|
102
|
-
format: z.literal("spacefast.wordpress-
|
|
147
|
+
format: z.literal("spacefast.wordpress-content-model"),
|
|
103
148
|
version: z.literal(1),
|
|
104
149
|
resources: z.array(wordpressContentResourceSchema).min(3).max(53),
|
|
105
150
|
})
|
|
@@ -134,7 +179,7 @@ export const wordpressProgramIrV1Schema = z
|
|
|
134
179
|
context.addIssue({
|
|
135
180
|
code: "custom",
|
|
136
181
|
path: ["resources"],
|
|
137
|
-
message: `WordPress
|
|
182
|
+
message: `WordPress content models require exactly one native ${required} resource`,
|
|
138
183
|
});
|
|
139
184
|
}
|
|
140
185
|
const native = wordpress.resources.find((resource) => resource.kind === required);
|
|
@@ -248,9 +293,9 @@ export const contentCacheProfileV1Schema = z.discriminatedUnion("kind", [
|
|
|
248
293
|
})
|
|
249
294
|
.strict(),
|
|
250
295
|
]);
|
|
251
|
-
const abilityRoutePathSchema = appRoutePathSchema({ subject: "Content
|
|
296
|
+
const abilityRoutePathSchema = appRoutePathSchema({ subject: "Content model routes" });
|
|
252
297
|
export const contentAbilityRouteAdmissionV1Schema = z.enum(["authenticated", "public"]);
|
|
253
|
-
export const
|
|
298
|
+
export const contentModelRouteSchema = z
|
|
254
299
|
.object({
|
|
255
300
|
id: stableIdSchema,
|
|
256
301
|
method: httpMethodSchema,
|
|
@@ -264,7 +309,7 @@ const contentAbilityExposureSchema = z.discriminatedUnion("kind", [
|
|
|
264
309
|
.object({
|
|
265
310
|
kind: z.literal("routed"),
|
|
266
311
|
named: z.literal(true),
|
|
267
|
-
route:
|
|
312
|
+
route: contentModelRouteSchema,
|
|
268
313
|
})
|
|
269
314
|
.strict(),
|
|
270
315
|
]);
|
|
@@ -405,7 +450,7 @@ export const contentAbilityReceiptV1Schema = z.discriminatedUnion("status", [
|
|
|
405
450
|
abilityId: contentAbilityIdSchema,
|
|
406
451
|
callId: stableIdSchema,
|
|
407
452
|
status: z.literal("succeeded"),
|
|
408
|
-
output:
|
|
453
|
+
output: contentModelJsonValueSchema,
|
|
409
454
|
invalidates: z.array(contentAbilityIdSchema).max(500),
|
|
410
455
|
})
|
|
411
456
|
.strict(),
|
|
@@ -435,65 +480,219 @@ export const contentExecutableRefV1Schema = z
|
|
|
435
480
|
.object({
|
|
436
481
|
id: stableIdSchema,
|
|
437
482
|
runtime: z.enum(["php", "quickjs"]),
|
|
438
|
-
artifact:
|
|
483
|
+
artifact: contentModelSha256Schema,
|
|
439
484
|
export: z.string().min(1).max(256),
|
|
440
485
|
})
|
|
441
486
|
.strict();
|
|
487
|
+
/**
|
|
488
|
+
* Which serializer stands between a source file and the blocks WordPress holds.
|
|
489
|
+
*
|
|
490
|
+
* The format is a property of the binding rather than of the request, because
|
|
491
|
+
* the file's extension decides it once at compile time and every later
|
|
492
|
+
* reconciliation has to agree. A caller cannot ask for the other serializer on
|
|
493
|
+
* a file this ContentModelRelease bound to one.
|
|
494
|
+
*/
|
|
495
|
+
export const CONTENT_SYNC_FORMATS = ["md", "html", "tsx"];
|
|
496
|
+
export const contentSyncFormatSchema = z.enum(CONTENT_SYNC_FORMATS);
|
|
497
|
+
/**
|
|
498
|
+
* Formats that compile one way. No serializer reads them back out of blocks, so
|
|
499
|
+
* their bindings carry the digest of the markup they produced and drift is
|
|
500
|
+
* settled by recompiling and comparing rather than by merging.
|
|
501
|
+
*/
|
|
502
|
+
export const CONTENT_SYNC_COMPILE_FORMATS = new Set(["tsx"]);
|
|
503
|
+
export function contentSyncFormatIsCompiled(format) {
|
|
504
|
+
return CONTENT_SYNC_COMPILE_FORMATS.has(format);
|
|
505
|
+
}
|
|
506
|
+
/** The source extension each serializer owns. */
|
|
507
|
+
export const CONTENT_SYNC_FORMAT_EXTENSIONS = new Map([
|
|
508
|
+
[".md", "md"],
|
|
509
|
+
[".markdown", "md"],
|
|
510
|
+
[".html", "html"],
|
|
511
|
+
[".htm", "html"],
|
|
512
|
+
[".tsx", "tsx"],
|
|
513
|
+
]);
|
|
514
|
+
/** The binding format a source path implies, or `null` when no serializer claims it. */
|
|
515
|
+
export function contentSyncFormatForSource(source) {
|
|
516
|
+
const dot = source.lastIndexOf(".");
|
|
517
|
+
if (dot === -1)
|
|
518
|
+
return null;
|
|
519
|
+
return CONTENT_SYNC_FORMAT_EXTENSIONS.get(source.slice(dot).toLowerCase()) ?? null;
|
|
520
|
+
}
|
|
442
521
|
export const contentSyncBindingV1Schema = z
|
|
443
522
|
.object({
|
|
444
523
|
id: contentSyncBindingIdSchema,
|
|
445
524
|
resourceId: contentResourceIdSchema,
|
|
446
525
|
fieldId: contentFieldIdSchema,
|
|
447
526
|
source: contentSyncSourcePathSchema,
|
|
527
|
+
publicPath: abilityRoutePathSchema.optional(),
|
|
528
|
+
// Optional for backward compatibility: a ContentModelRelease compiled before
|
|
529
|
+
// the HTML interchange format carried no `format`. The value is fully
|
|
530
|
+
// derivable from the source extension (the check below enforces they agree
|
|
531
|
+
// when both are present), so a stored binding without one is filled from the
|
|
532
|
+
// source rather than rejected — otherwise re-parsing a pre-format release
|
|
533
|
+
// would throw on live read paths (e.g. versionDeclaredContentModel).
|
|
534
|
+
format: contentSyncFormatSchema.optional(),
|
|
535
|
+
// The digest of the block markup this source compiled to. Deterministic —
|
|
536
|
+
// same source, same compiler, same digest — which is why it may live in a
|
|
537
|
+
// content-addressed release, and why a later read can compare it against
|
|
538
|
+
// what WordPress holds to see whether the two have parted ways.
|
|
539
|
+
compiled: z.object({ sha256: contentModelSha256Schema }).strict().optional(),
|
|
540
|
+
documentSeed: z
|
|
541
|
+
.object({ text: z.string().max(1_000_000), sha256: contentModelSha256Schema })
|
|
542
|
+
.strict()
|
|
543
|
+
.optional(),
|
|
448
544
|
slug: z.string().trim().min(1).max(200),
|
|
545
|
+
})
|
|
546
|
+
.strict()
|
|
547
|
+
.superRefine((binding, context) => {
|
|
548
|
+
const derived = contentSyncFormatForSource(binding.source);
|
|
549
|
+
if (binding.format === undefined) {
|
|
550
|
+
if (derived === null) {
|
|
551
|
+
context.addIssue({
|
|
552
|
+
code: "custom",
|
|
553
|
+
path: ["format"],
|
|
554
|
+
message: "sync binding source extension has no known format",
|
|
555
|
+
});
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else if (derived !== binding.format) {
|
|
560
|
+
context.addIssue({
|
|
561
|
+
code: "custom",
|
|
562
|
+
path: ["format"],
|
|
563
|
+
message: "sync binding format must match its source file extension",
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
const format = binding.format ?? derived;
|
|
567
|
+
if (format === null)
|
|
568
|
+
return;
|
|
569
|
+
if (contentSyncFormatIsCompiled(format)) {
|
|
570
|
+
if (binding.compiled === undefined) {
|
|
571
|
+
context.addIssue({
|
|
572
|
+
code: "custom",
|
|
573
|
+
path: ["compiled"],
|
|
574
|
+
message: "a one-way sync binding must carry the digest of its compiled markup",
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
else if (binding.compiled !== undefined) {
|
|
579
|
+
context.addIssue({
|
|
580
|
+
code: "custom",
|
|
581
|
+
path: ["compiled"],
|
|
582
|
+
message: "only one-way sync bindings carry a compiled digest",
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
.transform((binding) => ({
|
|
587
|
+
...binding,
|
|
588
|
+
// SAFETY: the superRefine above already rejected the only case where this
|
|
589
|
+
// could be null — an absent `format` whose source extension names no
|
|
590
|
+
// serializer — so by the time a value reaches the transform, an absent
|
|
591
|
+
// format means the source resolves.
|
|
592
|
+
format: binding.format ?? contentSyncFormatForSource(binding.source),
|
|
593
|
+
}));
|
|
594
|
+
/**
|
|
595
|
+
* Where a collection puts a document that WordPress created and no file backs
|
|
596
|
+
* yet. Deterministic — it restates the glob the capsule declared — so it may
|
|
597
|
+
* live in a content-addressed release.
|
|
598
|
+
*/
|
|
599
|
+
export const contentSyncMaterializationV1Schema = z
|
|
600
|
+
.object({
|
|
601
|
+
resourceId: contentResourceIdSchema,
|
|
602
|
+
fieldId: contentFieldIdSchema,
|
|
603
|
+
/** Capsule-relative directory, no trailing slash. */
|
|
604
|
+
directory: z.string().trim().min(1).max(512),
|
|
605
|
+
/** The filename suffix the glob named, e.g. ".md". */
|
|
606
|
+
suffix: z.string().trim().min(2).max(16),
|
|
607
|
+
format: contentSyncFormatSchema,
|
|
449
608
|
})
|
|
450
609
|
.strict();
|
|
451
|
-
export const
|
|
610
|
+
export const contentModelReleaseV1Schema = z
|
|
452
611
|
.object({
|
|
453
|
-
format: z.literal(
|
|
454
|
-
version: z.literal(
|
|
455
|
-
revision:
|
|
612
|
+
format: z.literal(CONTENT_MODEL_FORMAT),
|
|
613
|
+
version: z.literal(CONTENT_MODEL_VERSION),
|
|
614
|
+
revision: contentModelSha256Schema,
|
|
456
615
|
schemas: z.array(contentSchemaDefinitionSchema).min(1).max(500),
|
|
457
|
-
wordpress:
|
|
616
|
+
wordpress: wordpressContentModelIrV1Schema,
|
|
458
617
|
tables: z.array(contentTableIrV1Schema).max(100),
|
|
459
618
|
abilities: z.array(contentAbilityDescriptorV1Schema).max(500),
|
|
460
619
|
pages: z.array(contentClientPageIrV1Schema).max(10_000),
|
|
461
620
|
hooks: z.array(contentExecutableRefV1Schema).max(500),
|
|
462
621
|
syncBindings: z.array(contentSyncBindingV1Schema).max(10_000),
|
|
622
|
+
// A release published before materializations existed carries no such key.
|
|
623
|
+
// The strict schema is re-parsed against stored release JSON on live read
|
|
624
|
+
// paths (versionDeclaredContentModel), so this must default rather than be
|
|
625
|
+
// required — the PHP runtime already treats a missing key as "nothing
|
|
626
|
+
// materializes here".
|
|
627
|
+
materializations: z.array(contentSyncMaterializationV1Schema).max(100).default([]),
|
|
463
628
|
})
|
|
464
629
|
.strict()
|
|
465
|
-
.superRefine((
|
|
466
|
-
const schemas = new Map(
|
|
467
|
-
if (schemas.size !==
|
|
630
|
+
.superRefine((contentModel, context) => {
|
|
631
|
+
const schemas = new Map(contentModel.schemas.map((schema) => [schema.id, schema.sha256]));
|
|
632
|
+
if (schemas.size !== contentModel.schemas.length) {
|
|
468
633
|
context.addIssue({
|
|
469
634
|
code: "custom",
|
|
470
635
|
path: ["schemas"],
|
|
471
|
-
message: "
|
|
636
|
+
message: "content model schema ids must be unique",
|
|
472
637
|
});
|
|
473
638
|
}
|
|
474
|
-
const resources = new Map(
|
|
475
|
-
const tables = new Set(
|
|
639
|
+
const resources = new Map(contentModel.wordpress.resources.map((resource) => [resource.id, resource]));
|
|
640
|
+
const tables = new Set(contentModel.tables.map((table) => table.id));
|
|
476
641
|
const ids = [
|
|
477
|
-
...
|
|
478
|
-
...
|
|
479
|
-
...
|
|
480
|
-
...
|
|
481
|
-
...
|
|
482
|
-
...
|
|
642
|
+
...contentModel.wordpress.resources.map((resource) => resource.id),
|
|
643
|
+
...contentModel.tables.map((table) => table.id),
|
|
644
|
+
...contentModel.abilities.map((ability) => ability.id),
|
|
645
|
+
...contentModel.pages.map((page) => page.id),
|
|
646
|
+
...contentModel.hooks.map((hook) => hook.id),
|
|
647
|
+
...contentModel.syncBindings.map((binding) => binding.id),
|
|
483
648
|
];
|
|
484
649
|
if (new Set(ids).size !== ids.length) {
|
|
485
|
-
context.addIssue({
|
|
650
|
+
context.addIssue({
|
|
651
|
+
code: "custom",
|
|
652
|
+
message: "stable content model ids must be globally unique",
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const abilityNames = contentModel.abilities.map(({ id }) => contentAbilityWordPressName(id));
|
|
656
|
+
if (new Set(abilityNames).size !== abilityNames.length) {
|
|
657
|
+
context.addIssue({
|
|
658
|
+
code: "custom",
|
|
659
|
+
path: ["abilities"],
|
|
660
|
+
message: "ability WordPress names must be unique",
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
// A content Ability that flattens onto a platform Ability's WordPress name
|
|
664
|
+
// is a second row under one name: the runtime engine already publishes that
|
|
665
|
+
// set (`zero-wp-users`, `zero-storage`), and the merged catalog would carry
|
|
666
|
+
// two rows the consumer keys by the same id. Reject it here so the collision
|
|
667
|
+
// never reaches `zeroAbilityCatalog` or the types `sf zero types` generates.
|
|
668
|
+
const platformAbilityNames = new Set(ZERO_PLATFORM_ABILITIES.map((ability) => ability.name));
|
|
669
|
+
for (const [index, ability] of contentModel.abilities.entries()) {
|
|
670
|
+
if (platformAbilityNames.has(contentAbilityWordPressName(ability.id))) {
|
|
671
|
+
context.addIssue({
|
|
672
|
+
code: "custom",
|
|
673
|
+
path: ["abilities", index, "id"],
|
|
674
|
+
message: `ability ${ability.id} collides with a platform Ability the engine already publishes`,
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const abilityTypeNames = contentModel.abilities.map(({ id }) => contentAbilityTypeName(id));
|
|
679
|
+
if (new Set(abilityTypeNames).size !== abilityTypeNames.length) {
|
|
680
|
+
context.addIssue({
|
|
681
|
+
code: "custom",
|
|
682
|
+
path: ["abilities"],
|
|
683
|
+
message: "ability SDK type names must be unique",
|
|
684
|
+
});
|
|
486
685
|
}
|
|
487
686
|
const verifySchemaRef = (ref, path) => {
|
|
488
687
|
if (schemas.get(ref.id) !== ref.sha256) {
|
|
489
688
|
context.addIssue({
|
|
490
689
|
code: "custom",
|
|
491
690
|
path,
|
|
492
|
-
message: `schema reference ${ref.id} does not match a
|
|
691
|
+
message: `schema reference ${ref.id} does not match a content model schema`,
|
|
493
692
|
});
|
|
494
693
|
}
|
|
495
694
|
};
|
|
496
|
-
for (const [index, ability] of
|
|
695
|
+
for (const [index, ability] of contentModel.abilities.entries()) {
|
|
497
696
|
verifySchemaRef(ability.input, ["abilities", index, "input"]);
|
|
498
697
|
verifySchemaRef(ability.output, ["abilities", index, "output"]);
|
|
499
698
|
for (const [resourceIndex, resource] of [...ability.reads, ...ability.writes].entries()) {
|
|
@@ -523,10 +722,10 @@ export const contentProgramReleaseV1Schema = z
|
|
|
523
722
|
}
|
|
524
723
|
}
|
|
525
724
|
}
|
|
526
|
-
for (const [index, page] of
|
|
725
|
+
for (const [index, page] of contentModel.pages.entries()) {
|
|
527
726
|
verifySchemaRef(page.props, ["pages", index, "props"]);
|
|
528
727
|
}
|
|
529
|
-
for (const [index, binding] of
|
|
728
|
+
for (const [index, binding] of contentModel.syncBindings.entries()) {
|
|
530
729
|
const resource = resources.get(binding.resourceId);
|
|
531
730
|
const field = resource?.fields.find((candidate) => candidate.id === binding.fieldId);
|
|
532
731
|
if (!field) {
|
|
@@ -540,50 +739,79 @@ export const contentProgramReleaseV1Schema = z
|
|
|
540
739
|
context.addIssue({
|
|
541
740
|
code: "custom",
|
|
542
741
|
path: ["syncBindings", index, "fieldId"],
|
|
543
|
-
message: "
|
|
742
|
+
message: "source sync bindings require a block field",
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// One field materializes into one directory. A second answer to "where does
|
|
747
|
+
// a document WordPress just created belong" is the same class of mistake as
|
|
748
|
+
// two globs binding one field, and the runtime would have to guess.
|
|
749
|
+
const materialized = new Set();
|
|
750
|
+
for (const [index, materialization] of contentModel.materializations.entries()) {
|
|
751
|
+
const target = `${materialization.resourceId}${materialization.fieldId}`;
|
|
752
|
+
if (materialized.has(target)) {
|
|
753
|
+
context.addIssue({
|
|
754
|
+
code: "custom",
|
|
755
|
+
path: ["materializations", index],
|
|
756
|
+
message: `duplicate content materialization for ${materialization.resourceId}.${materialization.fieldId}`,
|
|
544
757
|
});
|
|
545
758
|
}
|
|
759
|
+
materialized.add(target);
|
|
546
760
|
}
|
|
547
761
|
});
|
|
548
|
-
export const
|
|
549
|
-
|
|
762
|
+
export const contentModelStageReceiptV1Schema = z
|
|
763
|
+
.object({
|
|
764
|
+
revision: contentModelSha256Schema,
|
|
765
|
+
artifactDigest: contentModelSha256Schema,
|
|
766
|
+
staged: z.boolean(),
|
|
767
|
+
})
|
|
768
|
+
.strict();
|
|
769
|
+
export const contentModelActivationReceiptV1Schema = z
|
|
770
|
+
.object({
|
|
771
|
+
revision: contentModelSha256Schema.nullable(),
|
|
772
|
+
tables: z.number().int().nonnegative(),
|
|
773
|
+
pages: z.number().int().nonnegative(),
|
|
774
|
+
})
|
|
775
|
+
.strict();
|
|
776
|
+
export const CONTENT_MODEL_ARTIFACT_FORMATS = {
|
|
777
|
+
model: CONTENT_MODEL_FORMAT,
|
|
550
778
|
executables: "spacefast.zero-executables",
|
|
551
779
|
sdk: "spacefast.sdk-descriptors",
|
|
552
780
|
routes: "spacefast.route-contributions",
|
|
553
|
-
requirements: "spacefast.
|
|
781
|
+
requirements: "spacefast.content-model-requirements",
|
|
554
782
|
};
|
|
555
|
-
export function
|
|
783
|
+
export function contentModelArtifactRefSchema(format) {
|
|
556
784
|
return z
|
|
557
785
|
.object({
|
|
558
786
|
format: z.literal(format),
|
|
559
787
|
version: z.literal(1),
|
|
560
|
-
inputDigest:
|
|
561
|
-
sha256:
|
|
788
|
+
inputDigest: contentModelSha256Schema,
|
|
789
|
+
sha256: contentModelSha256Schema,
|
|
562
790
|
bytes: z.number().int().nonnegative(),
|
|
563
791
|
mediaType: z.literal("application/json"),
|
|
564
792
|
})
|
|
565
793
|
.strict();
|
|
566
794
|
}
|
|
567
|
-
export const
|
|
795
|
+
export const compiledContentModelV1Schema = z
|
|
568
796
|
.object({
|
|
569
|
-
format: z.literal("spacefast.compiled-
|
|
797
|
+
format: z.literal("spacefast.compiled-content-model"),
|
|
570
798
|
version: z.literal(1),
|
|
571
|
-
|
|
572
|
-
executables:
|
|
573
|
-
sdk:
|
|
574
|
-
routes:
|
|
575
|
-
requirements:
|
|
799
|
+
model: contentModelArtifactRefSchema(CONTENT_MODEL_ARTIFACT_FORMATS.model),
|
|
800
|
+
executables: contentModelArtifactRefSchema(CONTENT_MODEL_ARTIFACT_FORMATS.executables),
|
|
801
|
+
sdk: contentModelArtifactRefSchema(CONTENT_MODEL_ARTIFACT_FORMATS.sdk),
|
|
802
|
+
routes: contentModelArtifactRefSchema(CONTENT_MODEL_ARTIFACT_FORMATS.routes),
|
|
803
|
+
requirements: contentModelArtifactRefSchema(CONTENT_MODEL_ARTIFACT_FORMATS.requirements),
|
|
576
804
|
})
|
|
577
805
|
.strict();
|
|
578
806
|
export const contentExecutableSetV1Schema = z
|
|
579
807
|
.object({
|
|
580
|
-
format: z.literal(
|
|
808
|
+
format: z.literal(CONTENT_MODEL_ARTIFACT_FORMATS.executables),
|
|
581
809
|
version: z.literal(1),
|
|
582
|
-
inputDigest:
|
|
810
|
+
inputDigest: contentModelSha256Schema,
|
|
583
811
|
executables: z.array(contentExecutableRefV1Schema).max(500),
|
|
584
812
|
})
|
|
585
813
|
.strict();
|
|
586
|
-
const contentSdkOperationDescriptorV1Schema = z
|
|
814
|
+
export const contentSdkOperationDescriptorV1Schema = z
|
|
587
815
|
.discriminatedUnion("kind", [
|
|
588
816
|
z
|
|
589
817
|
.object({
|
|
@@ -630,9 +858,9 @@ const contentSdkOperationDescriptorV1Schema = z
|
|
|
630
858
|
});
|
|
631
859
|
export const contentSdkDescriptorSetV1Schema = z
|
|
632
860
|
.object({
|
|
633
|
-
format: z.literal(
|
|
861
|
+
format: z.literal(CONTENT_MODEL_ARTIFACT_FORMATS.sdk),
|
|
634
862
|
version: z.literal(1),
|
|
635
|
-
inputDigest:
|
|
863
|
+
inputDigest: contentModelSha256Schema,
|
|
636
864
|
operations: z.array(contentSdkOperationDescriptorV1Schema),
|
|
637
865
|
sync: z.array(z
|
|
638
866
|
.object({
|
|
@@ -645,13 +873,13 @@ export const contentSdkDescriptorSetV1Schema = z
|
|
|
645
873
|
.strict();
|
|
646
874
|
export const contentRouteContributionSetV1Schema = z
|
|
647
875
|
.object({
|
|
648
|
-
format: z.literal(
|
|
876
|
+
format: z.literal(CONTENT_MODEL_ARTIFACT_FORMATS.routes),
|
|
649
877
|
version: z.literal(1),
|
|
650
|
-
inputDigest:
|
|
878
|
+
inputDigest: contentModelSha256Schema,
|
|
651
879
|
routes: z.array(z
|
|
652
880
|
.object({
|
|
653
881
|
abilityId: contentAbilityIdSchema,
|
|
654
|
-
route:
|
|
882
|
+
route: contentModelRouteSchema,
|
|
655
883
|
})
|
|
656
884
|
.strict()),
|
|
657
885
|
})
|