@amerged/ohmyhost-cli 0.1.36 → 0.1.38
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/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-LU7ONYZG.js → chunk-EWKJGJQU.js} +1379 -258
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +2 -2
- package/src/apps/product-cli/src/cli.ts +37 -1
- package/src/apps/product-cli/src/command.ts +228 -2
- package/src/apps/product-cli/src/composition.ts +4 -0
- package/src/apps/product-cli/src/product-api.ts +11 -1
- package/src/apps/product-cli/src/repository-init.ts +181 -9
- package/src/packages/contracts/package.json +6 -1
- package/src/packages/contracts/src/framework-admission-corpus.ts +15 -18
- package/src/packages/contracts/src/framework-build-script.mjs +10 -3
- package/src/packages/contracts/src/index.ts +3 -0
- package/src/packages/contracts/src/problem-policy.ts +2 -2
- package/src/packages/contracts/src/shared-mail.ts +236 -0
- package/src/packages/sdk-ts/package.json +2 -2
- package/src/packages/sdk-ts/src/generated/index.ts +112 -0
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +870 -0
- package/src/packages/sdk-ts/src/generated/shared-mail-response.ts +238 -0
- package/src/packages/sdk-ts/src/generated/types.gen.ts +1207 -0
- package/src/packages/sdk-ts/src/index.ts +2 -0
- package/src/packages/sdk-ts/src/shared-mail-response.ts +1 -0
- package/src/packages/sdk-ts/src/shared-mail.ts +225 -0
- package/src/packages/workos-auth-contracts/package.json +1 -1
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"exports": {
|
|
8
|
+
"./shared-mail": {
|
|
9
|
+
"source": "./src/shared-mail.ts",
|
|
10
|
+
"types": "./dist/shared-mail.d.ts",
|
|
11
|
+
"import": "./dist/shared-mail.js"
|
|
12
|
+
},
|
|
8
13
|
"./mcp-source-codec": {
|
|
9
14
|
"source": "./src/mcp-source-codec.ts",
|
|
10
15
|
"types": "./dist/mcp-source-codec.d.ts",
|
|
@@ -145,7 +150,7 @@
|
|
|
145
150
|
},
|
|
146
151
|
"scripts": {
|
|
147
152
|
"build": "tsc --project tsconfig.build.json",
|
|
148
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
153
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
149
154
|
"test": "vitest run --root ../.. packages/contracts/test",
|
|
150
155
|
"validate": "pnpm run typecheck && pnpm run test"
|
|
151
156
|
},
|
|
@@ -48,7 +48,7 @@ const CORPUS_NEXT_PAGE = `export default function Home() {
|
|
|
48
48
|
return <main>Hello from ohmyho.st</main>;
|
|
49
49
|
}
|
|
50
50
|
`;
|
|
51
|
-
// The config `create-next-app@16.4.
|
|
51
|
+
// The config `create-next-app@16.4.1 --yes --no-tailwind` writes: Cache Components with partial
|
|
52
52
|
// prefetching.
|
|
53
53
|
const CREATE_NEXT_APP_CONFIG = `import type { NextConfig } from "next";
|
|
54
54
|
|
|
@@ -124,10 +124,7 @@ export const FRAMEWORK_ADMISSION_CONFORMANCE_CORPUS: Readonly<{
|
|
|
124
124
|
Readonly<{ diagnostic: FrameworkConversionDiagnostic }>)[];
|
|
125
125
|
}> = Object.freeze({
|
|
126
126
|
admitted: Object.freeze([
|
|
127
|
-
corpusCase(
|
|
128
|
-
"Next.js 16.3.8 with npm on the edge runtime (the exact customer source)",
|
|
129
|
-
nextSource(),
|
|
130
|
-
),
|
|
127
|
+
corpusCase("Next.js 16.4.1 with npm on the edge runtime", nextSource()),
|
|
131
128
|
corpusCase("Next.js 17.0.0, a later stable major", nextSource({ declared: "17.0.0" })),
|
|
132
129
|
corpusCase(
|
|
133
130
|
"Vite ^8.2.0 resolved to 8.3.4 by pnpm-lock.yaml",
|
|
@@ -147,26 +144,26 @@ export const FRAMEWORK_ADMISSION_CONFORMANCE_CORPUS: Readonly<{
|
|
|
147
144
|
Object.freeze({
|
|
148
145
|
...corpusCase(
|
|
149
146
|
"Next.js ^16.3.0 with bun.lock, which ohmyho.st does not resolve",
|
|
150
|
-
nextSource({ declared: "^16.3.0", resolved: "16.4.
|
|
147
|
+
nextSource({ declared: "^16.3.0", resolved: "16.4.1", manager: "bun" }),
|
|
151
148
|
),
|
|
152
149
|
packages: Object.freeze(["next"] as const),
|
|
153
150
|
}),
|
|
154
151
|
]),
|
|
155
152
|
refused: Object.freeze([
|
|
156
153
|
refusedCase(
|
|
157
|
-
"Next.js 16.
|
|
158
|
-
nextSource({ declared: "16.
|
|
159
|
-
versionFinding("package-lock.json", "next", "16.
|
|
154
|
+
"Next.js 16.4.0 below the 16.x security minimum",
|
|
155
|
+
nextSource({ declared: "16.4.0" }),
|
|
156
|
+
versionFinding("package-lock.json", "next", "16.4.0", "16.4.1"),
|
|
160
157
|
),
|
|
161
158
|
refusedCase(
|
|
162
|
-
"Next.js 15.5.
|
|
163
|
-
nextSource({ declared: "15.5.
|
|
164
|
-
versionFinding("package.json", "next", "15.5.
|
|
159
|
+
"Next.js 15.5.28 declared with yarn, because no 15.x release is admitted",
|
|
160
|
+
nextSource({ declared: "15.5.28", manager: "yarn" }),
|
|
161
|
+
versionFinding("package.json", "next", "15.5.28", "16.4.1"),
|
|
165
162
|
),
|
|
166
163
|
refusedCase(
|
|
167
|
-
"Next.js ^16.3.0 resolved to 16.
|
|
168
|
-
nextSource({ declared: "^16.3.0", resolved: "16.
|
|
169
|
-
versionFinding("package-lock.json", "next", "16.
|
|
164
|
+
"Next.js ^16.3.0 resolved to 16.4.0 by package-lock.json",
|
|
165
|
+
nextSource({ declared: "^16.3.0", resolved: "16.4.0" }),
|
|
166
|
+
versionFinding("package-lock.json", "next", "16.4.0", "16.4.1"),
|
|
170
167
|
),
|
|
171
168
|
refusedCase(
|
|
172
169
|
"TanStack Start 1.168.59 resolved by pnpm-lock.yaml below its security minimum",
|
|
@@ -354,7 +351,7 @@ function nextSource(
|
|
|
354
351
|
): Readonly<Record<string, string>> {
|
|
355
352
|
const manager = options.manager ?? "npm";
|
|
356
353
|
const [packageManager, lockfile] = CORPUS_MANAGERS[manager];
|
|
357
|
-
const next = options.declared ?? "16.
|
|
354
|
+
const next = options.declared ?? "16.4.1";
|
|
358
355
|
const react = { react: "19.1.2", "react-dom": "19.1.2" };
|
|
359
356
|
const runtime = options.field === "devDependencies" ? react : { next, ...react };
|
|
360
357
|
return Object.freeze({
|
|
@@ -380,7 +377,7 @@ function nextSource(
|
|
|
380
377
|
}
|
|
381
378
|
|
|
382
379
|
/**
|
|
383
|
-
* The next.config.ts and Tailwind CSS files and packages `create-next-app@16.4.
|
|
380
|
+
* The next.config.ts and Tailwind CSS files and packages `create-next-app@16.4.1 --yes` writes, in
|
|
384
381
|
* an App Router source with the packageManager, lockfile and ohmyhost.yaml ohmyho.st requires;
|
|
385
382
|
* `tailwind: false` is its --no-tailwind output. On Turbopack, its default bundler, it adds only the
|
|
386
383
|
* Turbopack rule for Tailwind CSS; `postcss` adds the postcss.config.mjs and @tailwindcss/postcss it
|
|
@@ -390,7 +387,7 @@ function createNextAppSource(
|
|
|
390
387
|
options: Readonly<{ tailwind: boolean; postcss?: boolean }>,
|
|
391
388
|
): Readonly<Record<string, string>> {
|
|
392
389
|
const [packageManager, lockfile] = CORPUS_MANAGERS.npm;
|
|
393
|
-
const runtime = { next: "16.4.
|
|
390
|
+
const runtime = { next: "16.4.1", react: "19.3.0", "react-dom": "19.3.0" };
|
|
394
391
|
const postcss = options.postcss === true;
|
|
395
392
|
return Object.freeze({
|
|
396
393
|
"ohmyhost.yaml": corpusConfiguration("npm", ".open-next/assets", "edge"),
|
|
@@ -15,8 +15,15 @@ export const PINNED_BUN_VERSION = "1.2.22";
|
|
|
15
15
|
* upper bound: later patches, minors and majors reach the real build, which reports adapter failures.
|
|
16
16
|
* Denied ranges are half-open [introduced, fixed); a null fix denies every later release.
|
|
17
17
|
*
|
|
18
|
-
* next: GHSA-
|
|
19
|
-
*
|
|
18
|
+
* next: GHSA-2464-fhr4-w9xw (critical, remote code execution through the Image Optimization API with
|
|
19
|
+
* SVG and sharp before 0.35.5) and GHSA-p3m5-59mp-jv5r (high, denial of service through Server
|
|
20
|
+
* Actions), both fixed in 15.5.28 and 16.4.1; GHSA-673q-mjm6-r5xr (critical, remote code execution
|
|
21
|
+
* through `use cache` functions that return typed arrays, Next.js 16 only, fixed in 16.4.1).
|
|
22
|
+
* There is no 15.x floor: the pinned OpenNext has refused Next.js 15 since 21 October 2026, so every 15.x
|
|
23
|
+
* release is refused with the minimum 16.4.1 instead of sending a customer to 15.5.28 and a failing build.
|
|
24
|
+
* Earlier floors: GHSA-4jqv-mc3x-m676 and GHSA-mcj8-r9mp-w47p (fixed in 15.5.27 and 16.3.8);
|
|
25
|
+
* GHSA-cjq9-62q9-8jv4, GHSA-f87g-xv8r-7p7x, GHSA-3w37-wq28-93x7, GHSA-h694-7cp9-m8p3 and
|
|
26
|
+
* GHSA-39w2-rjm5-chcv (16.3.8).
|
|
20
27
|
* @tanstack/react-start: GHSA-qx66-fv34-fjm8 (fixed in 1.168.60).
|
|
21
28
|
* vite: GHSA-64vr-g452-qvp3 (fixed in 5.4.6), the only advisory affecting `vite build` output.
|
|
22
29
|
* @type {FrameworkVersionPolicy}
|
|
@@ -27,7 +34,7 @@ export const FRAMEWORK_VERSION_POLICY = Object.freeze({
|
|
|
27
34
|
floors: Object.freeze(["14.4.0"]),
|
|
28
35
|
denied: Object.freeze([]),
|
|
29
36
|
}),
|
|
30
|
-
next: Object.freeze({ floors: Object.freeze(["
|
|
37
|
+
next: Object.freeze({ floors: Object.freeze(["16.4.1"]), denied: Object.freeze([]) }),
|
|
31
38
|
vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
|
|
32
39
|
"@tanstack/react-start": Object.freeze({
|
|
33
40
|
floors: Object.freeze(["1.168.60"]),
|
|
@@ -209,6 +209,9 @@ function normalizeEgressOrigins(value: readonly string[] | undefined): readonly
|
|
|
209
209
|
function forbiddenEgressHostname(hostname: string): boolean {
|
|
210
210
|
const normalized = hostname.toLowerCase();
|
|
211
211
|
return (
|
|
212
|
+
/^ohmyho-(?:mail|storage)-gateway-[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*\.workers\.dev\.?$/u.test(
|
|
213
|
+
normalized,
|
|
214
|
+
) ||
|
|
212
215
|
normalized === "localhost" ||
|
|
213
216
|
normalized.endsWith(".localhost") ||
|
|
214
217
|
normalized.endsWith(".local") ||
|
|
@@ -313,14 +313,14 @@ export const PROBLEM_POLICY = {
|
|
|
313
313
|
status: 409,
|
|
314
314
|
retryable: false,
|
|
315
315
|
action:
|
|
316
|
-
"
|
|
316
|
+
"Read the workspace sender resource and project binding. Reuse requires explicit domain-use authority, target-project editing and a current verified application identity with the same registrable main domain. Read the current revision before changing settings. Detach a shared project binding explicitly; legacy domain deletion cannot retire a resource used by another project.",
|
|
317
317
|
title: "Mail domain conflict",
|
|
318
318
|
},
|
|
319
319
|
mail_domain_required: {
|
|
320
320
|
status: 409,
|
|
321
321
|
retryable: false,
|
|
322
322
|
action:
|
|
323
|
-
"This app declares mail.enabled. Configure
|
|
323
|
+
"This app declares mail.enabled. Configure a sender with 'ohmyhost mail setup' or explicitly attach an authorized workspace sender using the project's Prod environment ID. Reuse first requires proof of the intended application hostname with the same registrable main domain; declare and verify that identity before mail-gated deployment. Then plan again.",
|
|
324
324
|
title: "Mail domain required",
|
|
325
325
|
},
|
|
326
326
|
storage_jurisdiction_conflict: {
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
export interface SharedMailResponseContext {
|
|
2
|
+
readonly action: string;
|
|
3
|
+
readonly organizationId?: string;
|
|
4
|
+
readonly projectId?: string;
|
|
5
|
+
readonly environmentId?: string;
|
|
6
|
+
readonly mailDomainId?: string;
|
|
7
|
+
}
|
|
8
|
+
const ULID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
|
|
9
|
+
const HOST = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u;
|
|
10
|
+
|
|
11
|
+
/** Strict validation keeps provider data and out-of-scope resources out of client guidance. */
|
|
12
|
+
export function assertSharedMailResponse(value: unknown, input: SharedMailResponseContext): void {
|
|
13
|
+
const v = object(value);
|
|
14
|
+
if (input.action === "domains_list") {
|
|
15
|
+
exact(v, ["items"]);
|
|
16
|
+
if (!Array.isArray(v["items"]) || v["items"].length > 100) invalid();
|
|
17
|
+
for (const item of v["items"])
|
|
18
|
+
assertSharedMailResponse(item, {
|
|
19
|
+
...input,
|
|
20
|
+
action: "domain_get",
|
|
21
|
+
mailDomainId: String(object(item)["domain_id"]),
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (["domain_get", "domain_create", "domain_retire"].includes(input.action)) {
|
|
26
|
+
exact(v, [
|
|
27
|
+
"domain_id",
|
|
28
|
+
"organization_id",
|
|
29
|
+
"domain",
|
|
30
|
+
"registrable_domain",
|
|
31
|
+
"revision",
|
|
32
|
+
"status",
|
|
33
|
+
"sending",
|
|
34
|
+
"receiving",
|
|
35
|
+
"configured_at",
|
|
36
|
+
"observed_at",
|
|
37
|
+
"dns_records",
|
|
38
|
+
"bindings_count",
|
|
39
|
+
]);
|
|
40
|
+
id(v["domain_id"]);
|
|
41
|
+
id(v["organization_id"]);
|
|
42
|
+
if (
|
|
43
|
+
!("organizationId" in input) ||
|
|
44
|
+
v["organization_id"] !== input.organizationId ||
|
|
45
|
+
("mailDomainId" in input && v["domain_id"] !== input.mailDomainId)
|
|
46
|
+
)
|
|
47
|
+
invalid();
|
|
48
|
+
host(v["domain"]);
|
|
49
|
+
host(v["registrable_domain"]);
|
|
50
|
+
positive(v["revision"]);
|
|
51
|
+
member(v["status"], ["active", "retiring", "retired"]);
|
|
52
|
+
for (const k of ["sending", "receiving"])
|
|
53
|
+
member(v[k], ["disabled", "pending", "ready", "failed"]);
|
|
54
|
+
time(v["configured_at"]);
|
|
55
|
+
if (v["observed_at"] !== null) time(v["observed_at"]);
|
|
56
|
+
if (!Number.isSafeInteger(v["bindings_count"]) || Number(v["bindings_count"]) < 0) invalid();
|
|
57
|
+
if (!Array.isArray(v["dns_records"])) invalid();
|
|
58
|
+
for (const entry of v["dns_records"]) {
|
|
59
|
+
const r = object(entry);
|
|
60
|
+
exact(r, ["type", "name", "value", "purpose", "status"], ["priority"]);
|
|
61
|
+
member(r["type"], ["TXT", "MX", "CNAME"]);
|
|
62
|
+
for (const k of ["name", "value", "purpose", "status"])
|
|
63
|
+
if (typeof r[k] !== "string" || /[\r\n\0]/u.test(r[k])) invalid();
|
|
64
|
+
if (
|
|
65
|
+
r["priority"] !== undefined &&
|
|
66
|
+
(!Number.isSafeInteger(r["priority"]) ||
|
|
67
|
+
Number(r["priority"]) < 0 ||
|
|
68
|
+
Number(r["priority"]) > 65535)
|
|
69
|
+
)
|
|
70
|
+
invalid();
|
|
71
|
+
if (r["type"] === "MX" && r["priority"] === undefined) invalid();
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (["binding_get", "binding_set", "binding_detach"].includes(input.action)) {
|
|
76
|
+
exact(v, [
|
|
77
|
+
"mail_domain_id",
|
|
78
|
+
"organization_id",
|
|
79
|
+
"project_id",
|
|
80
|
+
"environment_id",
|
|
81
|
+
"revision",
|
|
82
|
+
"sending",
|
|
83
|
+
"sender_addresses",
|
|
84
|
+
"default_from",
|
|
85
|
+
"default_reply_to",
|
|
86
|
+
"legacy",
|
|
87
|
+
"status",
|
|
88
|
+
"application_domain_identity",
|
|
89
|
+
]);
|
|
90
|
+
for (const k of ["mail_domain_id", "organization_id", "project_id", "environment_id"]) id(v[k]);
|
|
91
|
+
if (
|
|
92
|
+
!("projectId" in input) ||
|
|
93
|
+
!("environmentId" in input) ||
|
|
94
|
+
v["project_id"] !== input.projectId ||
|
|
95
|
+
v["environment_id"] !== input.environmentId ||
|
|
96
|
+
("mailDomainId" in input && v["mail_domain_id"] !== input.mailDomainId)
|
|
97
|
+
)
|
|
98
|
+
invalid();
|
|
99
|
+
positive(v["revision"]);
|
|
100
|
+
for (const k of ["sending", "legacy"]) if (typeof v[k] !== "boolean") invalid();
|
|
101
|
+
member(v["status"], ["active", "detached", "suspended"]);
|
|
102
|
+
if (
|
|
103
|
+
!Array.isArray(v["sender_addresses"]) ||
|
|
104
|
+
v["sender_addresses"].length > 32 ||
|
|
105
|
+
new Set(v["sender_addresses"]).size !== v["sender_addresses"].length
|
|
106
|
+
)
|
|
107
|
+
invalid();
|
|
108
|
+
for (const a of v["sender_addresses"]) mailbox(a);
|
|
109
|
+
for (const k of ["default_from", "default_reply_to"]) if (v[k] !== null) mailbox(v[k]);
|
|
110
|
+
if (
|
|
111
|
+
!v["legacy"] &&
|
|
112
|
+
(!v["sender_addresses"].includes(v["default_from"]) || v["sender_addresses"].length === 0)
|
|
113
|
+
)
|
|
114
|
+
invalid();
|
|
115
|
+
if (v["application_domain_identity"] !== null) {
|
|
116
|
+
const identity = object(v["application_domain_identity"]);
|
|
117
|
+
exact(identity, ["hostname", "registrable_domain", "revision", "status"]);
|
|
118
|
+
host(identity["hostname"]);
|
|
119
|
+
host(identity["registrable_domain"]);
|
|
120
|
+
positive(identity["revision"]);
|
|
121
|
+
member(identity["status"], ["pending", "verified", "expired", "revoked"]);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (
|
|
126
|
+
["identity_get", "identity_declare", "identity_verify", "identity_revoke"].includes(
|
|
127
|
+
input.action,
|
|
128
|
+
)
|
|
129
|
+
) {
|
|
130
|
+
exact(v, [
|
|
131
|
+
"organization_id",
|
|
132
|
+
"project_id",
|
|
133
|
+
"hostname",
|
|
134
|
+
"registrable_domain",
|
|
135
|
+
"status",
|
|
136
|
+
"revision",
|
|
137
|
+
"challenge",
|
|
138
|
+
"verified_at",
|
|
139
|
+
"expires_at",
|
|
140
|
+
]);
|
|
141
|
+
id(v["organization_id"]);
|
|
142
|
+
id(v["project_id"]);
|
|
143
|
+
if (!("projectId" in input) || v["project_id"] !== input.projectId) invalid();
|
|
144
|
+
host(v["hostname"]);
|
|
145
|
+
host(v["registrable_domain"]);
|
|
146
|
+
positive(v["revision"]);
|
|
147
|
+
member(v["status"], ["pending", "verified", "expired", "revoked"]);
|
|
148
|
+
const challenge = object(v["challenge"]);
|
|
149
|
+
exact(challenge, ["type", "name", "value"]);
|
|
150
|
+
if (
|
|
151
|
+
challenge["type"] !== "TXT" ||
|
|
152
|
+
typeof challenge["name"] !== "string" ||
|
|
153
|
+
challenge["name"] !== `_omh-project.${v["hostname"]}` ||
|
|
154
|
+
typeof challenge["value"] !== "string" ||
|
|
155
|
+
challenge["value"].length < 16 ||
|
|
156
|
+
challenge["value"].length > 255 ||
|
|
157
|
+
/[\r\n\0]/u.test(challenge["value"])
|
|
158
|
+
)
|
|
159
|
+
invalid();
|
|
160
|
+
if (v["verified_at"] !== null) time(v["verified_at"]);
|
|
161
|
+
time(v["expires_at"]);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (input.action === "domain_dns_authorize" || input.action === "identity_authorize") {
|
|
165
|
+
exact(v, ["authorization_id", "authorization_url", "expires_at"]);
|
|
166
|
+
id(v["authorization_id"]);
|
|
167
|
+
time(v["expires_at"]);
|
|
168
|
+
if (
|
|
169
|
+
typeof v["authorization_url"] !== "string" ||
|
|
170
|
+
!/^https:\/\/dash\.cloudflare\.com\/oauth2\/auth\?/u.test(v["authorization_url"])
|
|
171
|
+
)
|
|
172
|
+
invalid();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
exact(v, ["status", "zone", "scopes", "expires_at"]);
|
|
176
|
+
member(v["status"], ["not_authorized", "pending", "authorized", "expired", "revoked"]);
|
|
177
|
+
if (
|
|
178
|
+
!Array.isArray(v["scopes"]) ||
|
|
179
|
+
v["scopes"].length > 2 ||
|
|
180
|
+
new Set(v["scopes"]).size !== v["scopes"].length ||
|
|
181
|
+
v["scopes"].some((s) => s !== "dns.write" && s !== "zone.read")
|
|
182
|
+
)
|
|
183
|
+
invalid();
|
|
184
|
+
if (v["zone"] !== null) host(v["zone"]);
|
|
185
|
+
if (v["expires_at"] !== null) time(v["expires_at"]);
|
|
186
|
+
if (
|
|
187
|
+
v["status"] === "not_authorized" &&
|
|
188
|
+
(v["zone"] !== null || v["expires_at"] !== null || v["scopes"].length)
|
|
189
|
+
)
|
|
190
|
+
invalid();
|
|
191
|
+
}
|
|
192
|
+
function object(v: unknown): Record<string, unknown> {
|
|
193
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) invalid();
|
|
194
|
+
return v as Record<string, unknown>;
|
|
195
|
+
}
|
|
196
|
+
function exact(
|
|
197
|
+
v: Record<string, unknown>,
|
|
198
|
+
required: readonly string[],
|
|
199
|
+
optional: readonly string[] = [],
|
|
200
|
+
) {
|
|
201
|
+
if (
|
|
202
|
+
required.some((k) => !(k in v)) ||
|
|
203
|
+
Object.keys(v).some((k) => !required.includes(k) && !optional.includes(k))
|
|
204
|
+
)
|
|
205
|
+
invalid();
|
|
206
|
+
}
|
|
207
|
+
function member(v: unknown, values: readonly string[]) {
|
|
208
|
+
if (typeof v !== "string" || !values.includes(v)) invalid();
|
|
209
|
+
}
|
|
210
|
+
function id(v: unknown) {
|
|
211
|
+
if (typeof v !== "string" || !ULID.test(v)) invalid();
|
|
212
|
+
}
|
|
213
|
+
function host(v: unknown) {
|
|
214
|
+
if (typeof v !== "string" || !HOST.test(v)) invalid();
|
|
215
|
+
}
|
|
216
|
+
function positive(v: unknown) {
|
|
217
|
+
if (!Number.isSafeInteger(v) || Number(v) < 1) invalid();
|
|
218
|
+
}
|
|
219
|
+
function time(v: unknown) {
|
|
220
|
+
if (typeof v !== "string" || !Number.isFinite(Date.parse(v)) || new Date(v).toISOString() !== v)
|
|
221
|
+
invalid();
|
|
222
|
+
}
|
|
223
|
+
function mailbox(v: unknown) {
|
|
224
|
+
if (
|
|
225
|
+
typeof v !== "string" ||
|
|
226
|
+
v.length > 254 ||
|
|
227
|
+
!/^[A-Za-z0-9.!#$%&'*+\-/=?^_`{|}~]+@[a-z0-9.-]+\.[a-z]{2,63}$/u.test(v) ||
|
|
228
|
+
v.startsWith(".") ||
|
|
229
|
+
v.includes("..") ||
|
|
230
|
+
v.includes(".@")
|
|
231
|
+
)
|
|
232
|
+
invalid();
|
|
233
|
+
}
|
|
234
|
+
function invalid(): never {
|
|
235
|
+
throw new TypeError("Shared mail response contract invalid");
|
|
236
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ohmyhost/sdk-ts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"build": "tsc --project tsconfig.build.json",
|
|
15
15
|
"check": "pnpm --dir ../.. sdk-ts:check",
|
|
16
16
|
"generate": "pnpm --dir ../.. sdk-ts:generate",
|
|
17
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
17
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
18
18
|
"test": "pnpm run check && vitest run --root ../.. packages/sdk-ts"
|
|
19
19
|
},
|
|
20
20
|
"license": "Apache-2.0"
|