@smartcrab/contracts-management 0.1.0

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 (73) hide show
  1. package/LICENSE +202 -0
  2. package/dist/api-keys.d.ts +251 -0
  3. package/dist/api-keys.js +78 -0
  4. package/dist/audit-events.d.ts +94 -0
  5. package/dist/audit-events.js +45 -0
  6. package/dist/billing.d.ts +30 -0
  7. package/dist/billing.js +30 -0
  8. package/dist/clients.d.ts +328 -0
  9. package/dist/clients.js +141 -0
  10. package/dist/common.d.ts +95 -0
  11. package/dist/common.js +99 -0
  12. package/dist/connections.d.ts +201 -0
  13. package/dist/connections.js +107 -0
  14. package/dist/domains.d.ts +167 -0
  15. package/dist/domains.js +74 -0
  16. package/dist/environments.d.ts +194 -0
  17. package/dist/environments.js +101 -0
  18. package/dist/index.d.ts +27 -0
  19. package/dist/index.js +27 -0
  20. package/dist/primitives.d.ts +49 -0
  21. package/dist/primitives.js +56 -0
  22. package/dist/projects.d.ts +78 -0
  23. package/dist/projects.js +49 -0
  24. package/dist/rbac.d.ts +222 -0
  25. package/dist/rbac.js +226 -0
  26. package/dist/usage.d.ts +72 -0
  27. package/dist/usage.js +52 -0
  28. package/dist/user-jobs.d.ts +253 -0
  29. package/dist/user-jobs.js +95 -0
  30. package/dist/user-subresources.d.ts +161 -0
  31. package/dist/user-subresources.js +85 -0
  32. package/dist/users.d.ts +193 -0
  33. package/dist/users.js +112 -0
  34. package/dist/webhooks.d.ts +306 -0
  35. package/dist/webhooks.js +134 -0
  36. package/dist/workspaces.d.ts +172 -0
  37. package/dist/workspaces.js +77 -0
  38. package/package.json +42 -0
  39. package/src/api-keys.test.ts +86 -0
  40. package/src/api-keys.ts +97 -0
  41. package/src/audit-events.test.ts +44 -0
  42. package/src/audit-events.ts +57 -0
  43. package/src/billing.test.ts +57 -0
  44. package/src/billing.ts +46 -0
  45. package/src/clients.test.ts +169 -0
  46. package/src/clients.ts +182 -0
  47. package/src/common.test.ts +102 -0
  48. package/src/common.ts +127 -0
  49. package/src/connections.test.ts +65 -0
  50. package/src/connections.ts +134 -0
  51. package/src/domains.test.ts +48 -0
  52. package/src/domains.ts +104 -0
  53. package/src/environments.test.ts +67 -0
  54. package/src/environments.ts +133 -0
  55. package/src/index.ts +27 -0
  56. package/src/primitives.ts +100 -0
  57. package/src/projects.test.ts +49 -0
  58. package/src/projects.ts +72 -0
  59. package/src/rbac.test.ts +148 -0
  60. package/src/rbac.ts +250 -0
  61. package/src/secret-fields.test.ts +84 -0
  62. package/src/usage.test.ts +57 -0
  63. package/src/usage.ts +66 -0
  64. package/src/user-jobs.test.ts +86 -0
  65. package/src/user-jobs.ts +120 -0
  66. package/src/user-subresources.test.ts +115 -0
  67. package/src/user-subresources.ts +128 -0
  68. package/src/users.test.ts +91 -0
  69. package/src/users.ts +149 -0
  70. package/src/webhooks.test.ts +187 -0
  71. package/src/webhooks.ts +182 -0
  72. package/src/workspaces.test.ts +127 -0
  73. package/src/workspaces.ts +106 -0
package/dist/common.js ADDED
@@ -0,0 +1,99 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Common Management API envelope conventions (design.md §26.3):
4
+ *
5
+ * - cursor pagination, max page size 100
6
+ * - write APIs support `Idempotency-Key`
7
+ * - updates support ETag / `If-Match`
8
+ * - a Request ID is attached to every response
9
+ * - errors are returned as RFC 9457 Problem Details
10
+ *
11
+ * `request_id`: design.md §26.3's Problem Details example embeds
12
+ * `request_id` inside the error body. For success responses this package
13
+ * does not duplicate `request_id` into every resource/list schema — doing so
14
+ * would force every list item and every nested object to carry a redundant
15
+ * copy. Instead, success responses attach it the same way
16
+ * `@smartcrab/result`'s `problemDetailsResponse` already does for errors:
17
+ * an `X-Request-Id` response header (`requestIdHeaderSchema` below). Routes
18
+ * that need the id in the JSON body too may extend a resource schema with
19
+ * `problemDetailsSchema`'s `request_id` field; the header is the one
20
+ * guarantee this package encodes as "attached to all responses".
21
+ */
22
+ // ---------------------------------------------------------------------------
23
+ // Pagination (design.md §26.3: "cursor pagination", "最大page size 100")
24
+ // ---------------------------------------------------------------------------
25
+ export const DEFAULT_PAGE_SIZE = 25;
26
+ export const MAX_PAGE_SIZE = 100;
27
+ /**
28
+ * Query parameters shared by every `GET` list route. `limit` is coerced from
29
+ * its wire representation (a query string) to a number so callers can parse
30
+ * `URLSearchParams` values directly.
31
+ */
32
+ export const paginationQuerySchema = z.object({
33
+ cursor: z.string().min(1).max(2048).optional(),
34
+ limit: z.coerce.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),
35
+ });
36
+ /**
37
+ * Generic cursor-paginated list envelope: `{ items, next_cursor }`.
38
+ * `next_cursor` is `null` once the caller has reached the last page.
39
+ */
40
+ export const cursorPageSchema = (itemSchema) => z.strictObject({
41
+ items: z.array(itemSchema),
42
+ next_cursor: z.string().min(1).nullable(),
43
+ });
44
+ // ---------------------------------------------------------------------------
45
+ // Idempotency-Key (design.md §26.3: "write APIは`Idempotency-Key`対応")
46
+ // ---------------------------------------------------------------------------
47
+ export const idempotencyKeySchema = z.string().min(1).max(255);
48
+ /** Header carried by every `POST`/`PATCH`/`DELETE` write request. */
49
+ export const idempotencyKeyHeaderSchema = z.strictObject({
50
+ "idempotency-key": idempotencyKeySchema,
51
+ });
52
+ // ---------------------------------------------------------------------------
53
+ // ETag / If-Match (design.md §26.3: "updateはETag / `If-Match`対応")
54
+ // ---------------------------------------------------------------------------
55
+ /** An opaque entity tag, optionally weak (`W/"..."`) or quoted per RFC 9110 §8.8.3. */
56
+ export const etagSchema = z.string().min(1).max(512);
57
+ /** Header carried by every conditional `PATCH`/`DELETE` request. */
58
+ export const ifMatchHeaderSchema = z.strictObject({
59
+ "if-match": etagSchema,
60
+ });
61
+ // ---------------------------------------------------------------------------
62
+ // Request ID (design.md §26.3: "Request IDを全レスポンスへ付与")
63
+ // ---------------------------------------------------------------------------
64
+ /** `req_...` (design.md §7.5 / `@smartcrab/identifiers` `ID_PREFIXES.request`). */
65
+ export const requestIdSchema = z.string().regex(/^req_[a-z2-7]{32}$/);
66
+ export const requestIdHeaderSchema = z.strictObject({
67
+ "x-request-id": requestIdSchema,
68
+ });
69
+ // ---------------------------------------------------------------------------
70
+ // Problem Details (design.md §26.3, RFC 9457)
71
+ // ---------------------------------------------------------------------------
72
+ /**
73
+ * Matches `@smartcrab/result`'s `ProblemDetails` interface shape exactly
74
+ * (this package does not import `@smartcrab/result`, per assignment
75
+ * rules, so the shape is redefined here as a zod schema for request/response
76
+ * contract validation rather than reused as a TS type).
77
+ *
78
+ * ```json
79
+ * {
80
+ * "type": "https://docs.example-auth.com/errors/invalid-redirect-uri",
81
+ * "title": "Invalid redirect URI",
82
+ * "status": 400,
83
+ * "detail": "The redirect URI is not registered for this client.",
84
+ * "code": "invalid_redirect_uri",
85
+ * "request_id": "req_xxx"
86
+ * }
87
+ * ```
88
+ *
89
+ * Deliberately NOT `z.strictObject`: RFC 9457 explicitly allows arbitrary
90
+ * extension members alongside the standard ones.
91
+ */
92
+ export const problemDetailsSchema = z.object({
93
+ type: z.string().min(1),
94
+ title: z.string().min(1),
95
+ status: z.number().int().min(100).max(599),
96
+ detail: z.string().optional(),
97
+ code: z.string().optional(),
98
+ request_id: requestIdSchema.optional(),
99
+ });
@@ -0,0 +1,201 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * `provider_connections` (design.md §11.10, §20). `secret_ciphertext`,
4
+ * `secret_iv`, and `secret_key_version` are excluded: the first two per the
5
+ * assignment's forbidden-field list, the third as the internal encryption
6
+ * key-rotation generation for those ciphertexts — the same "internal
7
+ * routing/security machinery" category, not a resource attribute a
8
+ * dashboard/API consumer needs.
9
+ *
10
+ * The provider's own OAuth `client_id` (design.md §11.10's `client_id`
11
+ * column) is renamed here to `provider_client_id` to avoid colliding with
12
+ * this platform's own `Client` resource (`clients.id`, §11.5) — the two are
13
+ * unrelated identifiers that happen to share a column name in the schema.
14
+ */
15
+ export declare const CONNECTION_MODES: readonly ["managed", "byo"];
16
+ export declare const connectionModeSchema: z.ZodEnum<{
17
+ managed: "managed";
18
+ byo: "byo";
19
+ }>;
20
+ export type ConnectionMode = (typeof CONNECTION_MODES)[number];
21
+ export declare const CONNECTION_STATUSES: readonly ["active", "disabled"];
22
+ export declare const connectionStatusSchema: z.ZodEnum<{
23
+ active: "active";
24
+ disabled: "disabled";
25
+ }>;
26
+ export type ConnectionStatus = (typeof CONNECTION_STATUSES)[number];
27
+ export declare const connectionSchema: z.ZodObject<{
28
+ id: z.ZodString;
29
+ environment_id: z.ZodString;
30
+ provider: z.ZodEnum<{
31
+ google: "google";
32
+ apple: "apple";
33
+ github: "github";
34
+ microsoft: "microsoft";
35
+ generic_oidc: "generic_oidc";
36
+ }>;
37
+ mode: z.ZodEnum<{
38
+ managed: "managed";
39
+ byo: "byo";
40
+ }>;
41
+ provider_key: z.ZodString;
42
+ issuer: z.ZodNullable<z.ZodURL>;
43
+ provider_client_id: z.ZodString;
44
+ scopes: z.ZodArray<z.ZodString>;
45
+ profile_mapping: z.ZodRecord<z.ZodString, z.ZodString>;
46
+ status: z.ZodEnum<{
47
+ active: "active";
48
+ disabled: "disabled";
49
+ }>;
50
+ owner_confirmed: z.ZodBoolean;
51
+ created_at: z.ZodISODateTime;
52
+ updated_at: z.ZodISODateTime;
53
+ }, z.core.$strict>;
54
+ export type Connection = z.infer<typeof connectionSchema>;
55
+ export declare const connectionListQuerySchema: z.ZodObject<{
56
+ cursor: z.ZodOptional<z.ZodString>;
57
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
58
+ environment_id: z.ZodString;
59
+ provider: z.ZodOptional<z.ZodEnum<{
60
+ google: "google";
61
+ apple: "apple";
62
+ github: "github";
63
+ microsoft: "microsoft";
64
+ generic_oidc: "generic_oidc";
65
+ }>>;
66
+ }, z.core.$strip>;
67
+ export type ConnectionListQuery = z.infer<typeof connectionListQuerySchema>;
68
+ export declare const connectionListResponseSchema: z.ZodObject<{
69
+ items: z.ZodArray<z.ZodObject<{
70
+ id: z.ZodString;
71
+ environment_id: z.ZodString;
72
+ provider: z.ZodEnum<{
73
+ google: "google";
74
+ apple: "apple";
75
+ github: "github";
76
+ microsoft: "microsoft";
77
+ generic_oidc: "generic_oidc";
78
+ }>;
79
+ mode: z.ZodEnum<{
80
+ managed: "managed";
81
+ byo: "byo";
82
+ }>;
83
+ provider_key: z.ZodString;
84
+ issuer: z.ZodNullable<z.ZodURL>;
85
+ provider_client_id: z.ZodString;
86
+ scopes: z.ZodArray<z.ZodString>;
87
+ profile_mapping: z.ZodRecord<z.ZodString, z.ZodString>;
88
+ status: z.ZodEnum<{
89
+ active: "active";
90
+ disabled: "disabled";
91
+ }>;
92
+ owner_confirmed: z.ZodBoolean;
93
+ created_at: z.ZodISODateTime;
94
+ updated_at: z.ZodISODateTime;
95
+ }, z.core.$strict>>;
96
+ next_cursor: z.ZodNullable<z.ZodString>;
97
+ }, z.core.$strict>;
98
+ export type ConnectionListResponse = z.infer<typeof connectionListResponseSchema>;
99
+ /**
100
+ * `provider_client_secret` is write-only: it is the plaintext OAuth client
101
+ * secret issued by the upstream provider (§20.4 "Secret暗号化" — the server
102
+ * encrypts it into `secret_ciphertext` before persisting). Unlike the
103
+ * platform-generated client/webhook/API-key secrets, this value is supplied
104
+ * by the caller, not generated by the server, so it never appears in any
105
+ * response, not even a one-time-display response.
106
+ */
107
+ export declare const connectionCreateRequestSchema: z.ZodObject<{
108
+ environment_id: z.ZodString;
109
+ provider: z.ZodEnum<{
110
+ google: "google";
111
+ apple: "apple";
112
+ github: "github";
113
+ microsoft: "microsoft";
114
+ generic_oidc: "generic_oidc";
115
+ }>;
116
+ mode: z.ZodEnum<{
117
+ managed: "managed";
118
+ byo: "byo";
119
+ }>;
120
+ provider_key: z.ZodString;
121
+ issuer: z.ZodOptional<z.ZodURL>;
122
+ provider_client_id: z.ZodString;
123
+ provider_client_secret: z.ZodString;
124
+ scopes: z.ZodDefault<z.ZodArray<z.ZodString>>;
125
+ profile_mapping: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
126
+ }, z.core.$strict>;
127
+ export type ConnectionCreateRequest = z.infer<typeof connectionCreateRequestSchema>;
128
+ export declare const connectionCreateResponseSchema: z.ZodObject<{
129
+ id: z.ZodString;
130
+ environment_id: z.ZodString;
131
+ provider: z.ZodEnum<{
132
+ google: "google";
133
+ apple: "apple";
134
+ github: "github";
135
+ microsoft: "microsoft";
136
+ generic_oidc: "generic_oidc";
137
+ }>;
138
+ mode: z.ZodEnum<{
139
+ managed: "managed";
140
+ byo: "byo";
141
+ }>;
142
+ provider_key: z.ZodString;
143
+ issuer: z.ZodNullable<z.ZodURL>;
144
+ provider_client_id: z.ZodString;
145
+ scopes: z.ZodArray<z.ZodString>;
146
+ profile_mapping: z.ZodRecord<z.ZodString, z.ZodString>;
147
+ status: z.ZodEnum<{
148
+ active: "active";
149
+ disabled: "disabled";
150
+ }>;
151
+ owner_confirmed: z.ZodBoolean;
152
+ created_at: z.ZodISODateTime;
153
+ updated_at: z.ZodISODateTime;
154
+ }, z.core.$strict>;
155
+ export type ConnectionCreateResponse = z.infer<typeof connectionCreateResponseSchema>;
156
+ export declare const connectionUpdateRequestSchema: z.ZodObject<{
157
+ issuer: z.ZodOptional<z.ZodURL>;
158
+ provider_client_id: z.ZodOptional<z.ZodString>;
159
+ provider_client_secret: z.ZodOptional<z.ZodString>;
160
+ scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
161
+ profile_mapping: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
162
+ status: z.ZodOptional<z.ZodEnum<{
163
+ active: "active";
164
+ disabled: "disabled";
165
+ }>>;
166
+ owner_confirmed: z.ZodOptional<z.ZodBoolean>;
167
+ }, z.core.$strict>;
168
+ export type ConnectionUpdateRequest = z.infer<typeof connectionUpdateRequestSchema>;
169
+ export declare const connectionUpdateResponseSchema: z.ZodObject<{
170
+ id: z.ZodString;
171
+ environment_id: z.ZodString;
172
+ provider: z.ZodEnum<{
173
+ google: "google";
174
+ apple: "apple";
175
+ github: "github";
176
+ microsoft: "microsoft";
177
+ generic_oidc: "generic_oidc";
178
+ }>;
179
+ mode: z.ZodEnum<{
180
+ managed: "managed";
181
+ byo: "byo";
182
+ }>;
183
+ provider_key: z.ZodString;
184
+ issuer: z.ZodNullable<z.ZodURL>;
185
+ provider_client_id: z.ZodString;
186
+ scopes: z.ZodArray<z.ZodString>;
187
+ profile_mapping: z.ZodRecord<z.ZodString, z.ZodString>;
188
+ status: z.ZodEnum<{
189
+ active: "active";
190
+ disabled: "disabled";
191
+ }>;
192
+ owner_confirmed: z.ZodBoolean;
193
+ created_at: z.ZodISODateTime;
194
+ updated_at: z.ZodISODateTime;
195
+ }, z.core.$strict>;
196
+ export type ConnectionUpdateResponse = z.infer<typeof connectionUpdateResponseSchema>;
197
+ export declare const connectionDeleteResponseSchema: z.ZodObject<{
198
+ id: z.ZodString;
199
+ deleted: z.ZodLiteral<true>;
200
+ }, z.core.$strict>;
201
+ export type ConnectionDeleteResponse = z.infer<typeof connectionDeleteResponseSchema>;
@@ -0,0 +1,107 @@
1
+ import { httpsUrlSchema, socialProviderSchema } from "@smartcrab/contracts-public";
2
+ import { z } from "zod";
3
+ import { cursorPageSchema, paginationQuerySchema } from "./common.js";
4
+ import { connectionIdSchema, environmentIdSchema } from "./primitives.js";
5
+ /**
6
+ * `provider_connections` (design.md §11.10, §20). `secret_ciphertext`,
7
+ * `secret_iv`, and `secret_key_version` are excluded: the first two per the
8
+ * assignment's forbidden-field list, the third as the internal encryption
9
+ * key-rotation generation for those ciphertexts — the same "internal
10
+ * routing/security machinery" category, not a resource attribute a
11
+ * dashboard/API consumer needs.
12
+ *
13
+ * The provider's own OAuth `client_id` (design.md §11.10's `client_id`
14
+ * column) is renamed here to `provider_client_id` to avoid colliding with
15
+ * this platform's own `Client` resource (`clients.id`, §11.5) — the two are
16
+ * unrelated identifiers that happen to share a column name in the schema.
17
+ */
18
+ export const CONNECTION_MODES = ["managed", "byo"];
19
+ export const connectionModeSchema = z.enum(CONNECTION_MODES);
20
+ export const CONNECTION_STATUSES = ["active", "disabled"];
21
+ export const connectionStatusSchema = z.enum(CONNECTION_STATUSES);
22
+ /** A single non-whitespace OAuth scope token requested from the upstream provider. */
23
+ const providerScopeSchema = z.string().min(1).max(100).regex(/^\S+$/);
24
+ /** design.md §11.10 `provider_key`: distinguishes multiple connections of the same provider per environment. */
25
+ const providerKeySchema = z
26
+ .string()
27
+ .min(1)
28
+ .max(100)
29
+ .regex(/^[a-z0-9]+(?:[_-][a-z0-9]+)*$/);
30
+ /** Maps this platform's normalized profile fields to the provider's own claim/attribute names. */
31
+ const profileMappingSchema = z.record(z.string(), z.string());
32
+ export const connectionSchema = z.strictObject({
33
+ id: connectionIdSchema,
34
+ environment_id: environmentIdSchema,
35
+ provider: socialProviderSchema,
36
+ mode: connectionModeSchema,
37
+ provider_key: providerKeySchema,
38
+ /** Only meaningful for `provider: "generic_oidc"` (design.md §11.10 `issuer NULLABLE`). */
39
+ issuer: httpsUrlSchema.nullable(),
40
+ provider_client_id: z.string().min(1),
41
+ scopes: z.array(providerScopeSchema).max(20),
42
+ profile_mapping: profileMappingSchema,
43
+ status: connectionStatusSchema,
44
+ /**
45
+ * design.md §20.3: "Dashboard上で所有者が明示確認するまでConnectionを有効化
46
+ * しない" — a `generic_oidc` connection's owner-supplied issuer is a real
47
+ * SSRF surface, so it stays unusable until the Dashboard owner explicitly
48
+ * confirms it here. Always `true` for every other provider (nothing to
49
+ * confirm).
50
+ */
51
+ owner_confirmed: z.boolean(),
52
+ created_at: z.iso.datetime(),
53
+ updated_at: z.iso.datetime(),
54
+ });
55
+ // ---------------------------------------------------------------------------
56
+ // GET /connections
57
+ // ---------------------------------------------------------------------------
58
+ export const connectionListQuerySchema = paginationQuerySchema.extend({
59
+ environment_id: environmentIdSchema,
60
+ provider: socialProviderSchema.optional(),
61
+ });
62
+ export const connectionListResponseSchema = cursorPageSchema(connectionSchema);
63
+ // ---------------------------------------------------------------------------
64
+ // POST /connections
65
+ // ---------------------------------------------------------------------------
66
+ /**
67
+ * `provider_client_secret` is write-only: it is the plaintext OAuth client
68
+ * secret issued by the upstream provider (§20.4 "Secret暗号化" — the server
69
+ * encrypts it into `secret_ciphertext` before persisting). Unlike the
70
+ * platform-generated client/webhook/API-key secrets, this value is supplied
71
+ * by the caller, not generated by the server, so it never appears in any
72
+ * response, not even a one-time-display response.
73
+ */
74
+ export const connectionCreateRequestSchema = z.strictObject({
75
+ environment_id: environmentIdSchema,
76
+ provider: socialProviderSchema,
77
+ mode: connectionModeSchema,
78
+ provider_key: providerKeySchema,
79
+ issuer: httpsUrlSchema.optional(),
80
+ provider_client_id: z.string().min(1),
81
+ provider_client_secret: z.string().min(1),
82
+ scopes: z.array(providerScopeSchema).max(20).default([]),
83
+ profile_mapping: profileMappingSchema.default({}),
84
+ });
85
+ export const connectionCreateResponseSchema = connectionSchema;
86
+ // ---------------------------------------------------------------------------
87
+ // PATCH /connections/:connectionId
88
+ // ---------------------------------------------------------------------------
89
+ export const connectionUpdateRequestSchema = z.strictObject({
90
+ issuer: httpsUrlSchema.optional(),
91
+ provider_client_id: z.string().min(1).optional(),
92
+ /** Write-only; omit to leave the stored secret unchanged. */
93
+ provider_client_secret: z.string().min(1).optional(),
94
+ scopes: z.array(providerScopeSchema).max(20).optional(),
95
+ profile_mapping: profileMappingSchema.optional(),
96
+ status: connectionStatusSchema.optional(),
97
+ /** design.md §20.3: the Dashboard owner's explicit confirmation step (only meaningful for `generic_oidc`; see `connectionSchema.owner_confirmed`). */
98
+ owner_confirmed: z.boolean().optional(),
99
+ });
100
+ export const connectionUpdateResponseSchema = connectionSchema;
101
+ // ---------------------------------------------------------------------------
102
+ // DELETE /connections/:connectionId
103
+ // ---------------------------------------------------------------------------
104
+ export const connectionDeleteResponseSchema = z.strictObject({
105
+ id: connectionIdSchema,
106
+ deleted: z.literal(true),
107
+ });
@@ -0,0 +1,167 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * `custom_domains` (design.md §11.9, §8.2). `cloudflare_custom_hostname_id`
4
+ * is excluded: it is an internal pointer into the Cloudflare for SaaS API
5
+ * (the same "internal routing" category as the assignment's named
6
+ * `*_database_key`/`auth_partition_id` fields), not a customer-facing
7
+ * attribute — validation progress is fully described by `status` and
8
+ * `validation_errors` without it.
9
+ */
10
+ export declare const CUSTOM_DOMAIN_STATUSES: readonly ["pending", "active", "moved", "failed", "deleting"];
11
+ export declare const customDomainStatusSchema: z.ZodEnum<{
12
+ failed: "failed";
13
+ active: "active";
14
+ pending: "pending";
15
+ moved: "moved";
16
+ deleting: "deleting";
17
+ }>;
18
+ export type CustomDomainStatus = (typeof CUSTOM_DOMAIN_STATUSES)[number];
19
+ /** Canonicalize a custom hostname at an external boundary before persistence or routing. */
20
+ export declare const canonicalizeCustomHostname: (hostname: string) => string;
21
+ export declare const domainSchema: z.ZodObject<{
22
+ id: z.ZodString;
23
+ environment_id: z.ZodString;
24
+ hostname: z.ZodString;
25
+ status: z.ZodEnum<{
26
+ failed: "failed";
27
+ active: "active";
28
+ pending: "pending";
29
+ moved: "moved";
30
+ deleting: "deleting";
31
+ }>;
32
+ rp_enabled: z.ZodBoolean;
33
+ validation_errors: z.ZodArray<z.ZodString>;
34
+ created_at: z.ZodISODateTime;
35
+ updated_at: z.ZodISODateTime;
36
+ activated_at: z.ZodNullable<z.ZodISODateTime>;
37
+ }, z.core.$strict>;
38
+ export type Domain = z.infer<typeof domainSchema>;
39
+ export declare const domainListQuerySchema: z.ZodObject<{
40
+ cursor: z.ZodOptional<z.ZodString>;
41
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
42
+ environment_id: z.ZodString;
43
+ status: z.ZodOptional<z.ZodEnum<{
44
+ failed: "failed";
45
+ active: "active";
46
+ pending: "pending";
47
+ moved: "moved";
48
+ deleting: "deleting";
49
+ }>>;
50
+ }, z.core.$strip>;
51
+ export type DomainListQuery = z.infer<typeof domainListQuerySchema>;
52
+ export declare const domainListResponseSchema: z.ZodObject<{
53
+ items: z.ZodArray<z.ZodObject<{
54
+ id: z.ZodString;
55
+ environment_id: z.ZodString;
56
+ hostname: z.ZodString;
57
+ status: z.ZodEnum<{
58
+ failed: "failed";
59
+ active: "active";
60
+ pending: "pending";
61
+ moved: "moved";
62
+ deleting: "deleting";
63
+ }>;
64
+ rp_enabled: z.ZodBoolean;
65
+ validation_errors: z.ZodArray<z.ZodString>;
66
+ created_at: z.ZodISODateTime;
67
+ updated_at: z.ZodISODateTime;
68
+ activated_at: z.ZodNullable<z.ZodISODateTime>;
69
+ }, z.core.$strict>>;
70
+ next_cursor: z.ZodNullable<z.ZodString>;
71
+ }, z.core.$strict>;
72
+ export type DomainListResponse = z.infer<typeof domainListResponseSchema>;
73
+ export declare const domainCreateRequestSchema: z.ZodObject<{
74
+ environment_id: z.ZodString;
75
+ hostname: z.ZodString;
76
+ rp_enabled: z.ZodDefault<z.ZodBoolean>;
77
+ }, z.core.$strict>;
78
+ export type DomainCreateRequest = z.infer<typeof domainCreateRequestSchema>;
79
+ /** A freshly created domain always starts `pending` validation (design.md §11.9). */
80
+ export declare const domainCreateResponseSchema: z.ZodObject<{
81
+ id: z.ZodString;
82
+ environment_id: z.ZodString;
83
+ hostname: z.ZodString;
84
+ status: z.ZodEnum<{
85
+ failed: "failed";
86
+ active: "active";
87
+ pending: "pending";
88
+ moved: "moved";
89
+ deleting: "deleting";
90
+ }>;
91
+ rp_enabled: z.ZodBoolean;
92
+ validation_errors: z.ZodArray<z.ZodString>;
93
+ created_at: z.ZodISODateTime;
94
+ updated_at: z.ZodISODateTime;
95
+ activated_at: z.ZodNullable<z.ZodISODateTime>;
96
+ }, z.core.$strict>;
97
+ export type DomainCreateResponse = z.infer<typeof domainCreateResponseSchema>;
98
+ export declare const domainGetResponseSchema: z.ZodObject<{
99
+ id: z.ZodString;
100
+ environment_id: z.ZodString;
101
+ hostname: z.ZodString;
102
+ status: z.ZodEnum<{
103
+ failed: "failed";
104
+ active: "active";
105
+ pending: "pending";
106
+ moved: "moved";
107
+ deleting: "deleting";
108
+ }>;
109
+ rp_enabled: z.ZodBoolean;
110
+ validation_errors: z.ZodArray<z.ZodString>;
111
+ created_at: z.ZodISODateTime;
112
+ updated_at: z.ZodISODateTime;
113
+ activated_at: z.ZodNullable<z.ZodISODateTime>;
114
+ }, z.core.$strict>;
115
+ export type DomainGetResponse = z.infer<typeof domainGetResponseSchema>;
116
+ export declare const domainDeleteResponseSchema: z.ZodObject<{
117
+ id: z.ZodString;
118
+ deleted: z.ZodLiteral<true>;
119
+ }, z.core.$strict>;
120
+ export type DomainDeleteResponse = z.infer<typeof domainDeleteResponseSchema>;
121
+ /**
122
+ * design.md §8.3: `rp_enabled` switches both the WebAuthn RP ID and the hosted
123
+ * Interaction Domain to this custom domain. At most one per Environment.
124
+ */
125
+ export declare const domainUpdateRequestSchema: z.ZodObject<{
126
+ rp_enabled: z.ZodBoolean;
127
+ }, z.core.$strict>;
128
+ export type DomainUpdateRequest = z.infer<typeof domainUpdateRequestSchema>;
129
+ export declare const domainUpdateResponseSchema: z.ZodObject<{
130
+ id: z.ZodString;
131
+ environment_id: z.ZodString;
132
+ hostname: z.ZodString;
133
+ status: z.ZodEnum<{
134
+ failed: "failed";
135
+ active: "active";
136
+ pending: "pending";
137
+ moved: "moved";
138
+ deleting: "deleting";
139
+ }>;
140
+ rp_enabled: z.ZodBoolean;
141
+ validation_errors: z.ZodArray<z.ZodString>;
142
+ created_at: z.ZodISODateTime;
143
+ updated_at: z.ZodISODateTime;
144
+ activated_at: z.ZodNullable<z.ZodISODateTime>;
145
+ }, z.core.$strict>;
146
+ export type DomainUpdateResponse = z.infer<typeof domainUpdateResponseSchema>;
147
+ export declare const domainRetryValidationRequestSchema: z.ZodObject<{}, z.core.$strict>;
148
+ export type DomainRetryValidationRequest = z.infer<typeof domainRetryValidationRequestSchema>;
149
+ /** Retrying resets `status` to `pending` and clears prior `validation_errors`. */
150
+ export declare const domainRetryValidationResponseSchema: z.ZodObject<{
151
+ id: z.ZodString;
152
+ environment_id: z.ZodString;
153
+ hostname: z.ZodString;
154
+ status: z.ZodEnum<{
155
+ failed: "failed";
156
+ active: "active";
157
+ pending: "pending";
158
+ moved: "moved";
159
+ deleting: "deleting";
160
+ }>;
161
+ rp_enabled: z.ZodBoolean;
162
+ validation_errors: z.ZodArray<z.ZodString>;
163
+ created_at: z.ZodISODateTime;
164
+ updated_at: z.ZodISODateTime;
165
+ activated_at: z.ZodNullable<z.ZodISODateTime>;
166
+ }, z.core.$strict>;
167
+ export type DomainRetryValidationResponse = z.infer<typeof domainRetryValidationResponseSchema>;
@@ -0,0 +1,74 @@
1
+ import { z } from "zod";
2
+ import { cursorPageSchema, paginationQuerySchema } from "./common.js";
3
+ import { domainIdSchema, environmentIdSchema } from "./primitives.js";
4
+ /**
5
+ * `custom_domains` (design.md §11.9, §8.2). `cloudflare_custom_hostname_id`
6
+ * is excluded: it is an internal pointer into the Cloudflare for SaaS API
7
+ * (the same "internal routing" category as the assignment's named
8
+ * `*_database_key`/`auth_partition_id` fields), not a customer-facing
9
+ * attribute — validation progress is fully described by `status` and
10
+ * `validation_errors` without it.
11
+ */
12
+ export const CUSTOM_DOMAIN_STATUSES = ["pending", "active", "moved", "failed", "deleting"];
13
+ export const customDomainStatusSchema = z.enum(CUSTOM_DOMAIN_STATUSES);
14
+ const customHostnameSchema = z.string().min(1).max(253);
15
+ /** Canonicalize a custom hostname at an external boundary before persistence or routing. */
16
+ export const canonicalizeCustomHostname = (hostname) => hostname.toLowerCase();
17
+ export const domainSchema = z.strictObject({
18
+ id: domainIdSchema,
19
+ environment_id: environmentIdSchema,
20
+ hostname: customHostnameSchema,
21
+ status: customDomainStatusSchema,
22
+ /** design.md §11.9: whether this custom domain also serves as the WebAuthn RP ID host. */
23
+ rp_enabled: z.boolean(),
24
+ validation_errors: z.array(z.string().min(1)),
25
+ created_at: z.iso.datetime(),
26
+ updated_at: z.iso.datetime(),
27
+ activated_at: z.iso.datetime().nullable(),
28
+ });
29
+ // ---------------------------------------------------------------------------
30
+ // GET /domains
31
+ // ---------------------------------------------------------------------------
32
+ export const domainListQuerySchema = paginationQuerySchema.extend({
33
+ environment_id: environmentIdSchema,
34
+ status: customDomainStatusSchema.optional(),
35
+ });
36
+ export const domainListResponseSchema = cursorPageSchema(domainSchema);
37
+ // ---------------------------------------------------------------------------
38
+ // POST /domains
39
+ // ---------------------------------------------------------------------------
40
+ export const domainCreateRequestSchema = z.strictObject({
41
+ environment_id: environmentIdSchema,
42
+ hostname: customHostnameSchema,
43
+ rp_enabled: z.boolean().default(false),
44
+ });
45
+ /** A freshly created domain always starts `pending` validation (design.md §11.9). */
46
+ export const domainCreateResponseSchema = domainSchema;
47
+ // ---------------------------------------------------------------------------
48
+ // GET /domains/:domainId
49
+ // ---------------------------------------------------------------------------
50
+ export const domainGetResponseSchema = domainSchema;
51
+ // ---------------------------------------------------------------------------
52
+ // DELETE /domains/:domainId
53
+ // ---------------------------------------------------------------------------
54
+ export const domainDeleteResponseSchema = z.strictObject({
55
+ id: domainIdSchema,
56
+ deleted: z.literal(true),
57
+ });
58
+ // ---------------------------------------------------------------------------
59
+ // PATCH /domains/:domainId
60
+ // ---------------------------------------------------------------------------
61
+ /**
62
+ * design.md §8.3: `rp_enabled` switches both the WebAuthn RP ID and the hosted
63
+ * Interaction Domain to this custom domain. At most one per Environment.
64
+ */
65
+ export const domainUpdateRequestSchema = z.strictObject({
66
+ rp_enabled: z.boolean(),
67
+ });
68
+ export const domainUpdateResponseSchema = domainSchema;
69
+ // ---------------------------------------------------------------------------
70
+ // POST /domains/:domainId/retry-validation
71
+ // ---------------------------------------------------------------------------
72
+ export const domainRetryValidationRequestSchema = z.strictObject({});
73
+ /** Retrying resets `status` to `pending` and clears prior `validation_errors`. */
74
+ export const domainRetryValidationResponseSchema = domainSchema;