@lukoweb/apitogo 0.1.148 → 0.2.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/cli/cli.js +5533 -1850
- package/dist/declarations/cli/common/constants.d.ts +0 -1
- package/dist/declarations/cli/platform/client/platform-api.d.ts +8 -0
- package/dist/declarations/cli/platform/config.d.ts +0 -10
- package/package.json +4 -1
- package/src/app/main.tsx +0 -1
- package/src/config/v2/__fixtures__/scenario-a-docs-only.json +6 -0
- package/src/config/v2/__fixtures__/scenario-b-managed-origin.json +14 -0
- package/src/config/v2/__fixtures__/scenario-c-proxy-oidc.json +15 -0
- package/src/config/v2/__fixtures__/scenario-h-full-stack.json +28 -0
- package/src/config/v2/errors.ts +42 -0
- package/src/config/v2/index.ts +105 -0
- package/src/config/v2/init.ts +273 -0
- package/src/config/v2/layout.ts +237 -0
- package/src/config/v2/ledger.ts +219 -0
- package/src/config/v2/load.ts +70 -0
- package/src/config/v2/manifest.ts +293 -0
- package/src/config/v2/parse.ts +214 -0
- package/src/config/v2/paths.ts +54 -0
- package/src/config/v2/portal-profile.ts +88 -0
- package/src/config/v2/resolve.ts +186 -0
- package/src/config/v2/rules.ts +650 -0
- package/src/config/v2/schema.ts +211 -0
- package/src/config/v2/services.ts +644 -0
- package/src/config/v2/workspace.ts +117 -0
- package/src/config/v2/wrapper.ts +397 -0
- package/src/vite/html.ts +0 -2
- package/src/vite/plugin-theme.ts +23 -7
- package/src/app/font.geist.css +0 -73
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Config version 2 (Tenant Service Model, section 5; Reference, section 3).
|
|
5
|
+
*
|
|
6
|
+
* The schema is structural: shape, types, enums and ranges. Rules that relate fields to each other or to the
|
|
7
|
+
* outside world (dependencies between services, files that must exist, provider capabilities) belong to
|
|
8
|
+
* `apitogo check`.
|
|
9
|
+
*/
|
|
10
|
+
export const CONFIG_VERSION = 2;
|
|
11
|
+
|
|
12
|
+
const SECRET_REFERENCE = /^secret:[A-Za-z_][A-Za-z0-9_]*$/;
|
|
13
|
+
|
|
14
|
+
/** A reference to a project secret, never the secret itself. */
|
|
15
|
+
const secretRef = z.string().refine((value) => SECRET_REFERENCE.test(value), {
|
|
16
|
+
error:
|
|
17
|
+
'must be a secret reference such as "secret:NAME", not the secret itself. Store the value with `apitogo secrets set NAME`',
|
|
18
|
+
params: { code: "CONFIG_INLINE_SECRET" },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const httpsUrl = z.url({
|
|
22
|
+
protocol: /^https$/,
|
|
23
|
+
error: "must be an https:// URL",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const nonEmpty = z.string().min(1, { error: "must not be empty" });
|
|
27
|
+
|
|
28
|
+
/** A path relative to the config file. */
|
|
29
|
+
const path = nonEmpty;
|
|
30
|
+
const pathList = z.array(nonEmpty);
|
|
31
|
+
|
|
32
|
+
const enabled = z.boolean().optional();
|
|
33
|
+
|
|
34
|
+
export const PortalSchema = z.strictObject({
|
|
35
|
+
enabled,
|
|
36
|
+
dir: path.optional(),
|
|
37
|
+
name: z.string().min(1).max(80).optional(),
|
|
38
|
+
docs: z
|
|
39
|
+
.strictObject({
|
|
40
|
+
provider: z.enum(["apitogo", "external"]).optional(),
|
|
41
|
+
enabled,
|
|
42
|
+
content: pathList.optional(),
|
|
43
|
+
url: httpsUrl.optional(),
|
|
44
|
+
})
|
|
45
|
+
.optional(),
|
|
46
|
+
console: z.strictObject({ enabled }).optional(),
|
|
47
|
+
theme: path.optional(),
|
|
48
|
+
requireAuth: z
|
|
49
|
+
.union([z.boolean(), z.strictObject({ exclude: pathList })], {
|
|
50
|
+
error: 'must be true, false or { "exclude": ["/route", ...] }',
|
|
51
|
+
})
|
|
52
|
+
.optional(),
|
|
53
|
+
search: z.enum(["pagefind", "inkeep", "none"]).optional(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const GatewayOriginSchema = z.strictObject({
|
|
57
|
+
mode: z.enum(["managed", "proxy"]),
|
|
58
|
+
source: path.optional(),
|
|
59
|
+
runtime: z.enum(["nodejs", "python", "dotnet"]).optional(),
|
|
60
|
+
runtimeVersion: nonEmpty.optional(),
|
|
61
|
+
exclude: pathList.optional(),
|
|
62
|
+
url: httpsUrl.optional(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const GatewaySchema = z.strictObject({
|
|
66
|
+
enabled,
|
|
67
|
+
provider: nonEmpty.optional(),
|
|
68
|
+
origin: GatewayOriginSchema,
|
|
69
|
+
specs: z.array(path).min(1, { error: "needs at least one OpenAPI file" }),
|
|
70
|
+
auth: z
|
|
71
|
+
.strictObject({
|
|
72
|
+
mode: z.enum(["identity", "oidc"]),
|
|
73
|
+
metadataUrl: httpsUrl.optional(),
|
|
74
|
+
issuers: z.array(httpsUrl).optional(),
|
|
75
|
+
audiences: z.array(nonEmpty).optional(),
|
|
76
|
+
})
|
|
77
|
+
.optional(),
|
|
78
|
+
policies: z
|
|
79
|
+
.strictObject({
|
|
80
|
+
rateLimit: z
|
|
81
|
+
.strictObject({
|
|
82
|
+
calls: z.int().positive(),
|
|
83
|
+
periodSeconds: z.int().positive(),
|
|
84
|
+
})
|
|
85
|
+
.optional(),
|
|
86
|
+
quota: z
|
|
87
|
+
.strictObject({
|
|
88
|
+
calls: z.int().positive(),
|
|
89
|
+
periodDays: z.int().min(1),
|
|
90
|
+
})
|
|
91
|
+
.optional(),
|
|
92
|
+
cors: z.array(z.url()).optional(),
|
|
93
|
+
})
|
|
94
|
+
.optional(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const connectionId = z.string().regex(/^[a-z0-9-]{1,32}$/, {
|
|
98
|
+
error: "must be 1 to 32 lowercase letters, digits or hyphens",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const IdentitySchema = z.strictObject({
|
|
102
|
+
enabled,
|
|
103
|
+
provider: z.enum(["apitogo", "oidc"]).optional(),
|
|
104
|
+
native: z.boolean().optional(),
|
|
105
|
+
connections: z
|
|
106
|
+
.array(
|
|
107
|
+
z.strictObject({
|
|
108
|
+
id: connectionId,
|
|
109
|
+
type: z.literal("oidc"),
|
|
110
|
+
authority: httpsUrl,
|
|
111
|
+
clientId: nonEmpty,
|
|
112
|
+
clientSecret: secretRef,
|
|
113
|
+
scopes: z.array(nonEmpty).optional(),
|
|
114
|
+
displayName: nonEmpty.optional(),
|
|
115
|
+
}),
|
|
116
|
+
)
|
|
117
|
+
.optional(),
|
|
118
|
+
clients: z
|
|
119
|
+
.array(
|
|
120
|
+
z.strictObject({
|
|
121
|
+
id: nonEmpty,
|
|
122
|
+
type: z.enum(["spa", "web", "m2m"]),
|
|
123
|
+
redirectUris: z.array(nonEmpty).optional(),
|
|
124
|
+
postLogoutRedirectUris: z.array(nonEmpty).optional(),
|
|
125
|
+
allowedOrigins: z.array(nonEmpty).optional(),
|
|
126
|
+
scopes: z.array(nonEmpty).optional(),
|
|
127
|
+
audiences: z.array(nonEmpty).optional(),
|
|
128
|
+
secret: secretRef.optional(),
|
|
129
|
+
}),
|
|
130
|
+
)
|
|
131
|
+
.optional(),
|
|
132
|
+
issuer: httpsUrl.optional(),
|
|
133
|
+
clientId: nonEmpty.optional(),
|
|
134
|
+
providerOptions: z
|
|
135
|
+
.strictObject({
|
|
136
|
+
// The Identity design doc (8.7) does not name the token lifetime keys yet; tighten when it does.
|
|
137
|
+
tokens: z.record(z.string(), z.unknown()).optional(),
|
|
138
|
+
password: z
|
|
139
|
+
.strictObject({
|
|
140
|
+
minLength: z.int().min(8).max(128).optional(),
|
|
141
|
+
checkBreached: z.boolean().optional(),
|
|
142
|
+
})
|
|
143
|
+
.optional(),
|
|
144
|
+
signup: z
|
|
145
|
+
.strictObject({
|
|
146
|
+
mode: z.enum(["open", "closed", "invite"]).optional(),
|
|
147
|
+
allowedEmailDomains: z.array(nonEmpty).optional(),
|
|
148
|
+
})
|
|
149
|
+
.optional(),
|
|
150
|
+
mfa: z
|
|
151
|
+
.strictObject({
|
|
152
|
+
mode: z.enum(["off", "optional", "required"]).optional(),
|
|
153
|
+
})
|
|
154
|
+
.optional(),
|
|
155
|
+
branding: z
|
|
156
|
+
.strictObject({
|
|
157
|
+
logo: nonEmpty.optional(),
|
|
158
|
+
color: z
|
|
159
|
+
.string()
|
|
160
|
+
.regex(/^#[0-9a-fA-F]{6}$/, {
|
|
161
|
+
error: "must be a hex colour such as #1a2b3c",
|
|
162
|
+
})
|
|
163
|
+
.optional(),
|
|
164
|
+
})
|
|
165
|
+
.optional(),
|
|
166
|
+
audience: nonEmpty.optional(),
|
|
167
|
+
customDomain: z
|
|
168
|
+
.string()
|
|
169
|
+
.regex(/^(?=.{1,253}$)([a-z0-9-]+\.)+[a-z]{2,}$/, {
|
|
170
|
+
error: "must be a lowercase hostname such as id.acme.com",
|
|
171
|
+
})
|
|
172
|
+
.optional(),
|
|
173
|
+
})
|
|
174
|
+
.optional(),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* There is deliberately no `provider` field: plans entitlements follow the gateway provider and only
|
|
179
|
+
* `billing.provider` is selectable (decision 21).
|
|
180
|
+
*/
|
|
181
|
+
export const PlansSchema = z.strictObject({
|
|
182
|
+
enabled,
|
|
183
|
+
file: path.optional(),
|
|
184
|
+
billing: z
|
|
185
|
+
.strictObject({
|
|
186
|
+
provider: z.enum(["stripe", "none"]).optional(),
|
|
187
|
+
apiKey: secretRef.optional(),
|
|
188
|
+
webhookSecret: secretRef.optional(),
|
|
189
|
+
successUrl: httpsUrl.optional(),
|
|
190
|
+
cancelUrl: httpsUrl.optional(),
|
|
191
|
+
})
|
|
192
|
+
.optional(),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
export const ConfigV2Schema = z.strictObject({
|
|
196
|
+
$schema: z.string().optional(),
|
|
197
|
+
version: z.literal(CONFIG_VERSION, {
|
|
198
|
+
error: `must be ${CONFIG_VERSION}`,
|
|
199
|
+
}),
|
|
200
|
+
project: z
|
|
201
|
+
.strictObject({ id: nonEmpty.optional(), name: nonEmpty.optional() })
|
|
202
|
+
.optional(),
|
|
203
|
+
layout: z.enum(["standalone", "embedded"]).optional(),
|
|
204
|
+
portal: PortalSchema.optional(),
|
|
205
|
+
gateway: GatewaySchema.optional(),
|
|
206
|
+
identity: IdentitySchema.optional(),
|
|
207
|
+
plans: PlansSchema.optional(),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
export type ConfigV2 = z.infer<typeof ConfigV2Schema>;
|
|
211
|
+
export type Layout = NonNullable<ConfigV2["layout"]>;
|