@spacefast/common 0.0.3 → 0.0.6
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/connect-targets.d.ts +133 -0
- package/dist/agents/connect-targets.js +224 -0
- package/dist/brand.d.ts +1 -0
- package/dist/brand.js +5 -0
- package/dist/config/domains.d.ts +12 -6
- package/dist/config/domains.js +30 -38
- package/dist/contracts/access.d.ts +829 -129
- package/dist/contracts/access.js +771 -148
- package/dist/contracts/activity.d.ts +5 -5
- package/dist/contracts/activity.js +28 -2
- package/dist/contracts/annotations.d.ts +3 -5
- package/dist/contracts/api-keys.d.ts +9 -10
- package/dist/contracts/api-keys.js +9 -6
- package/dist/contracts/archives.d.ts +22 -16
- package/dist/contracts/archives.js +11 -4
- package/dist/contracts/billing.d.ts +21 -30
- package/dist/contracts/builds.d.ts +21 -17
- package/dist/contracts/builds.js +12 -0
- package/dist/contracts/channels.d.ts +2 -2
- package/dist/contracts/common.d.ts +7 -5
- package/dist/contracts/common.js +4 -0
- package/dist/contracts/continuation.d.ts +63 -0
- package/dist/contracts/continuation.js +91 -0
- package/dist/contracts/countries.d.ts +2 -0
- package/dist/contracts/countries.js +261 -0
- package/dist/contracts/deployments.d.ts +34 -34
- package/dist/contracts/device-auth.d.ts +4 -11
- package/dist/contracts/device-auth.js +4 -0
- package/dist/contracts/docs.d.ts +119 -0
- package/dist/contracts/docs.js +51 -0
- package/dist/contracts/domains.d.ts +295 -253
- package/dist/contracts/domains.js +26 -4
- package/dist/contracts/enums.d.ts +17 -18
- package/dist/contracts/enums.js +25 -14
- package/dist/contracts/error-code-meta.d.ts +84 -0
- package/dist/contracts/error-code-meta.js +99 -10
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +17 -0
- package/dist/contracts/events.d.ts +13 -13
- package/dist/contracts/features.d.ts +93 -23
- package/dist/contracts/features.js +178 -13
- package/dist/contracts/git.d.ts +3 -9
- package/dist/contracts/ids.d.ts +5 -0
- package/dist/contracts/ids.js +5 -0
- package/dist/contracts/internal.d.ts +3 -3
- package/dist/contracts/mcp.d.ts +34 -36
- package/dist/contracts/me.d.ts +8 -8
- package/dist/contracts/me.js +1 -1
- package/dist/contracts/oauth-resources.d.ts +19 -8
- package/dist/contracts/oauth-resources.js +44 -13
- package/dist/contracts/operations.d.ts +2 -2
- package/dist/contracts/plan-policy.d.ts +4 -4
- package/dist/contracts/platform.d.ts +3 -105
- package/dist/contracts/platform.js +0 -50
- package/dist/contracts/push-new.d.ts +4 -4
- package/dist/contracts/quotas.d.ts +2 -0
- package/dist/contracts/quotas.js +8 -0
- package/dist/contracts/repository-connections.d.ts +1 -3
- package/dist/contracts/resources.d.ts +112 -28
- package/dist/contracts/resources.js +26 -2
- package/dist/contracts/routes.d.ts +0 -2
- package/dist/contracts/runtime-api.d.ts +365 -43
- package/dist/contracts/runtime-api.js +275 -6
- package/dist/contracts/sites.d.ts +45 -28
- package/dist/contracts/sites.js +21 -0
- package/dist/contracts/space-config.d.ts +201 -2
- package/dist/contracts/space-config.js +135 -5
- package/dist/contracts/spaces.d.ts +423 -162
- package/dist/contracts/spaces.js +94 -8
- package/dist/contracts/superadmin-emails.d.ts +7 -5
- package/dist/contracts/superadmin-emails.js +8 -0
- package/dist/contracts/superadmin-queues.d.ts +32 -32
- package/dist/contracts/superadmin-spaces.d.ts +418 -24
- package/dist/contracts/superadmin-spaces.js +24 -0
- package/dist/contracts/superadmin-tenants.d.ts +8 -8
- package/dist/contracts/superadmin.d.ts +17 -104
- package/dist/contracts/superadmin.js +6 -14
- package/dist/contracts/tags.d.ts +42 -41
- package/dist/contracts/tags.js +2 -1
- package/dist/contracts/teams.d.ts +40 -23
- package/dist/contracts/teams.js +20 -1
- package/dist/contracts/theme-json.d.ts +30 -0
- package/dist/contracts/theme-json.js +48 -0
- package/dist/contracts/transfers.d.ts +9 -9
- package/dist/contracts/variables.d.ts +14 -16
- package/dist/contracts/variables.js +0 -5
- package/dist/contracts/webhooks.d.ts +6 -4
- package/dist/contracts/webhooks.js +1 -1
- package/dist/contracts/zero.d.ts +13 -18
- package/dist/docs/agent-prose.d.ts +43 -3
- package/dist/docs/agent-prose.js +153 -10
- package/dist/docs/agent-setup.js +48 -15
- package/dist/docs/agent-solutions.d.ts +5 -5
- package/dist/docs/agent-solutions.js +2 -2
- package/dist/docs/catalog.d.ts +650 -0
- package/dist/docs/catalog.js +758 -0
- package/dist/docs/error-docs.d.ts +0 -2
- package/dist/docs/error-docs.js +89 -35
- package/dist/docs/index-build.d.ts +17 -0
- package/dist/docs/index-build.js +24 -0
- package/dist/docs/search.d.ts +54 -0
- package/dist/docs/search.js +96 -0
- package/dist/docs/skill-distribution.d.ts +5 -3
- package/dist/docs/skill-distribution.js +8 -8
- package/dist/utils/access-match.d.ts +39 -0
- package/dist/utils/access-match.js +296 -0
- package/dist/utils/auth-redirect.d.ts +3 -1
- package/dist/utils/auth-redirect.js +21 -1
- package/dist/utils/build-settings.d.ts +6 -0
- package/dist/utils/build-settings.js +429 -46
- package/dist/utils/claim-token.d.ts +8 -0
- package/dist/utils/claim-token.js +23 -2
- package/dist/utils/content-type.d.ts +1 -0
- package/dist/utils/content-type.js +1 -1
- package/dist/utils/credential-policy.d.ts +54 -0
- package/dist/utils/{access-policy.js → credential-policy.js} +28 -2
- package/dist/utils/gate-theme.d.ts +61 -0
- package/dist/utils/gate-theme.js +217 -0
- package/dist/utils/git-repository.d.ts +0 -1
- package/dist/utils/git-repository.js +0 -3
- package/dist/utils/id-hints.d.ts +8 -0
- package/dist/utils/id-hints.js +61 -0
- package/dist/utils/local-space-state.d.ts +91 -0
- package/dist/utils/local-space-state.js +251 -0
- package/dist/utils/oauth-signed-query.d.ts +8 -0
- package/dist/utils/oauth-signed-query.js +26 -0
- package/dist/utils/publish-detection.d.ts +0 -8
- package/dist/utils/publish-detection.js +0 -93
- package/dist/utils/publish-policy.d.ts +6 -5
- package/dist/utils/publish-policy.js +71 -7
- package/dist/utils/query-keys.d.ts +17 -6
- package/dist/utils/query-keys.js +27 -4
- package/dist/utils/runtime-paths.d.ts +0 -1
- package/dist/utils/runtime-paths.js +0 -1
- package/dist/utils/runtime-upload-batch.d.ts +18 -0
- package/dist/utils/runtime-upload-batch.js +90 -0
- package/dist/utils/runtime-upload.d.ts +2 -0
- package/dist/utils/runtime-upload.js +5 -0
- package/dist/utils/space-config.d.ts +4 -0
- package/dist/utils/space-config.js +10 -1
- package/dist/utils/space-theme.d.ts +2 -0
- package/dist/utils/space-theme.js +9 -0
- package/dist/utils/static-runtime-policy.d.ts +1 -1
- package/dist/utils/static-runtime-policy.js +47 -15
- package/dist/utils/upload-session.d.ts +1 -1
- package/dist/utils/wpcom-auth-redirects.d.ts +32 -2
- package/dist/utils/wpcom-auth-redirects.js +26 -10
- package/dist/vocabulary.d.ts +130 -30
- package/dist/vocabulary.js +77 -36
- package/package.json +16 -1
- package/dist/contracts/account.d.ts +0 -28
- package/dist/contracts/account.js +0 -54
- package/dist/contracts/intercom.d.ts +0 -6
- package/dist/contracts/intercom.js +0 -5
- package/dist/contracts/telemetry.d.ts +0 -2
- package/dist/contracts/telemetry.js +0 -1
- package/dist/utils/access-policy.d.ts +0 -39
- package/dist/utils/error-display.d.ts +0 -7
- package/dist/utils/error-display.js +0 -42
|
@@ -216,6 +216,224 @@ export function detectPlatformConfigs(input) {
|
|
|
216
216
|
cloudflarePagesConfig(input),
|
|
217
217
|
].filter((config) => Boolean(config));
|
|
218
218
|
}
|
|
219
|
+
const SUPPORTED_REDIRECT_STATUSES = new Set([200, 301, 302, 303, 307, 308, 404]);
|
|
220
|
+
const NETLIFY_CONDITION_KEYS = new Map([
|
|
221
|
+
["country", "Country"],
|
|
222
|
+
["language", "Language"],
|
|
223
|
+
["cookie", "Cookie"],
|
|
224
|
+
]);
|
|
225
|
+
const VERCEL_TRAILING_REGEX_SPLAT = /\(\.\*\)$/;
|
|
226
|
+
const VERCEL_TRAILING_NAMED_SPLAT = /:([A-Za-z][A-Za-z0-9_]*)[*+]$/;
|
|
227
|
+
const UNSUPPORTED_PATTERN_CHARS = /[(){}+?!$^[\]|\\]/;
|
|
228
|
+
function booleanField(value, key) {
|
|
229
|
+
const field = value[key];
|
|
230
|
+
return typeof field === "boolean" ? field : null;
|
|
231
|
+
}
|
|
232
|
+
// Converts a Vercel path-to-regexp source into `_redirects` pattern syntax.
|
|
233
|
+
// Supported: literal segments, `:name` segment params, and one trailing
|
|
234
|
+
// catch-all (`:name*`, `:name+`, or `(.*)`). Anything else returns null.
|
|
235
|
+
function vercelSourceToPattern(source) {
|
|
236
|
+
let splat = null;
|
|
237
|
+
let pattern = source;
|
|
238
|
+
const regexSplat = pattern.match(VERCEL_TRAILING_REGEX_SPLAT);
|
|
239
|
+
if (regexSplat) {
|
|
240
|
+
pattern = pattern.slice(0, -regexSplat[0].length);
|
|
241
|
+
splat = "$1";
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const namedSplat = pattern.match(VERCEL_TRAILING_NAMED_SPLAT);
|
|
245
|
+
if (namedSplat?.[1]) {
|
|
246
|
+
pattern = pattern.slice(0, -namedSplat[0].length);
|
|
247
|
+
splat = `:${namedSplat[1]}`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (UNSUPPORTED_PATTERN_CHARS.test(pattern) || pattern.includes("*")) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
return { pattern: splat === null ? pattern : `${pattern}*`, splat };
|
|
254
|
+
}
|
|
255
|
+
function vercelDestination(destination, splat) {
|
|
256
|
+
let converted = destination;
|
|
257
|
+
if (splat === "$1") {
|
|
258
|
+
converted = converted.replaceAll("$1", ":splat");
|
|
259
|
+
}
|
|
260
|
+
else if (splat) {
|
|
261
|
+
// `:name`, `:name*`, or `:name+` — but not a longer name sharing the prefix.
|
|
262
|
+
const reference = new RegExp(`\\${splat}[*+]?(?![A-Za-z0-9_])`, "g");
|
|
263
|
+
converted = converted.replace(reference, ":splat");
|
|
264
|
+
}
|
|
265
|
+
// Unresolved regex captures cannot be expressed in `_redirects`.
|
|
266
|
+
return /\$\d/.test(converted) ? null : converted;
|
|
267
|
+
}
|
|
268
|
+
function vercelRedirectStatus(rule) {
|
|
269
|
+
const statusCode = numberField(rule, "statusCode");
|
|
270
|
+
if (statusCode !== null) {
|
|
271
|
+
return statusCode;
|
|
272
|
+
}
|
|
273
|
+
return booleanField(rule, "permanent") ? 308 : 307;
|
|
274
|
+
}
|
|
275
|
+
function convertVercelRoutingRule(rule, kind, out) {
|
|
276
|
+
const source = stringField(rule, "source");
|
|
277
|
+
const destination = stringField(rule, "destination");
|
|
278
|
+
const label = `vercel.json ${kind} ${source ?? "(missing source)"}`;
|
|
279
|
+
if (!source || !destination) {
|
|
280
|
+
out.notes.push(`${label}: skipped — missing source or destination.`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if ("has" in rule || "missing" in rule) {
|
|
284
|
+
out.notes.push(`${label}: skipped — has/missing conditions are not supported.`);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const converted = vercelSourceToPattern(source);
|
|
288
|
+
if (!converted) {
|
|
289
|
+
out.notes.push(`${label}: skipped — the source pattern does not map to _redirects syntax.`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const convertedDestination = vercelDestination(destination, converted.splat);
|
|
293
|
+
if (!convertedDestination) {
|
|
294
|
+
out.notes.push(`${label}: skipped — the destination uses unsupported regex captures.`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const status = kind === "rewrite" ? 200 : vercelRedirectStatus(rule);
|
|
298
|
+
if (!SUPPORTED_REDIRECT_STATUSES.has(status)) {
|
|
299
|
+
out.notes.push(`${label}: skipped — status ${status} is not supported.`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
out.redirectLines.push(`${converted.pattern} ${convertedDestination} ${status}`);
|
|
303
|
+
}
|
|
304
|
+
function convertNetlifyRedirect(rule, out) {
|
|
305
|
+
const from = stringField(rule, "from");
|
|
306
|
+
const to = stringField(rule, "to");
|
|
307
|
+
const label = `netlify.toml redirect ${from ?? "(missing from)"}`;
|
|
308
|
+
if (!from || !to) {
|
|
309
|
+
out.notes.push(`${label}: skipped — missing from or to.`);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if ("signed" in rule || "headers" in rule) {
|
|
313
|
+
out.notes.push(`${label}: skipped — signed proxying and proxy headers are not supported.`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const status = numberField(rule, "status") ?? 301;
|
|
317
|
+
if (!SUPPORTED_REDIRECT_STATUSES.has(status)) {
|
|
318
|
+
out.notes.push(`${label}: skipped — status ${status} is not supported.`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const queryTokens = [];
|
|
322
|
+
const query = objectField(rule, "query");
|
|
323
|
+
if (query) {
|
|
324
|
+
for (const [name, capture] of Object.entries(query)) {
|
|
325
|
+
if (typeof capture !== "string" || !capture.startsWith(":")) {
|
|
326
|
+
out.notes.push(`${label}: skipped — literal query matches are not supported.`);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
queryTokens.push(`${name}=${capture}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const conditionTokens = [];
|
|
333
|
+
const conditions = objectField(rule, "conditions");
|
|
334
|
+
if (conditions) {
|
|
335
|
+
for (const [name, values] of Object.entries(conditions)) {
|
|
336
|
+
const mapped = NETLIFY_CONDITION_KEYS.get(name.toLowerCase());
|
|
337
|
+
const list = Array.isArray(values)
|
|
338
|
+
? values.filter((value) => typeof value === "string")
|
|
339
|
+
: null;
|
|
340
|
+
if (!mapped || !list || list.length === 0) {
|
|
341
|
+
out.notes.push(`${label}: skipped — the ${name} condition is not supported.`);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
conditionTokens.push(`${mapped}=${list.join(",")}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const force = booleanField(rule, "force") ? "!" : "";
|
|
348
|
+
out.redirectLines.push([from, ...queryTokens, to, `${status}${force}`, ...conditionTokens].join(" "));
|
|
349
|
+
}
|
|
350
|
+
function headerBlock(matcher, headers) {
|
|
351
|
+
return [matcher, ...headers.map(([name, value]) => ` ${name}: ${value}`)].join("\n");
|
|
352
|
+
}
|
|
353
|
+
function convertVercelHeaderRule(rule, out) {
|
|
354
|
+
const source = stringField(rule, "source");
|
|
355
|
+
const label = `vercel.json headers ${source ?? "(missing source)"}`;
|
|
356
|
+
if (!source) {
|
|
357
|
+
out.notes.push(`${label}: skipped — missing source.`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if ("has" in rule || "missing" in rule) {
|
|
361
|
+
out.notes.push(`${label}: skipped — has/missing conditions are not supported.`);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const converted = vercelSourceToPattern(source);
|
|
365
|
+
if (!converted) {
|
|
366
|
+
out.notes.push(`${label}: skipped — the source pattern does not map to _headers syntax.`);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const entries = rule.headers;
|
|
370
|
+
const headers = (Array.isArray(entries) ? entries : [])
|
|
371
|
+
.map((entry) => {
|
|
372
|
+
const header = entry && typeof entry === "object" ? entry : null;
|
|
373
|
+
const key = stringField(header, "key");
|
|
374
|
+
const value = header && typeof header.value === "string" ? header.value : null;
|
|
375
|
+
return key && value !== null ? [key, value] : null;
|
|
376
|
+
})
|
|
377
|
+
.filter((entry) => entry !== null);
|
|
378
|
+
if (headers.length === 0) {
|
|
379
|
+
out.notes.push(`${label}: skipped — no header values.`);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
out.headerBlocks.push(headerBlock(converted.pattern, headers));
|
|
383
|
+
}
|
|
384
|
+
function convertNetlifyHeaderRule(rule, out) {
|
|
385
|
+
const matcher = stringField(rule, "for");
|
|
386
|
+
const label = `netlify.toml headers ${matcher ?? "(missing for)"}`;
|
|
387
|
+
const values = objectField(rule, "values");
|
|
388
|
+
if (!matcher || !values) {
|
|
389
|
+
out.notes.push(`${label}: skipped — missing for or values.`);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const headers = Object.entries(values)
|
|
393
|
+
.map((entry) => {
|
|
394
|
+
const [name, value] = entry;
|
|
395
|
+
if (typeof value === "string") {
|
|
396
|
+
return [name, value];
|
|
397
|
+
}
|
|
398
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
399
|
+
return [name, value.join(", ")];
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
})
|
|
403
|
+
.filter((entry) => entry !== null);
|
|
404
|
+
if (headers.length === 0) {
|
|
405
|
+
out.notes.push(`${label}: skipped — no header values.`);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
out.headerBlocks.push(headerBlock(matcher, headers));
|
|
409
|
+
}
|
|
410
|
+
export function convertPlatformRouting(configs) {
|
|
411
|
+
const out = { redirectLines: [], headerBlocks: [], notes: [] };
|
|
412
|
+
for (const config of configs) {
|
|
413
|
+
if (config.platform === "vercel") {
|
|
414
|
+
for (const rule of config.redirects) {
|
|
415
|
+
convertVercelRoutingRule(rule, "redirect", out);
|
|
416
|
+
}
|
|
417
|
+
for (const rule of config.rewrites) {
|
|
418
|
+
convertVercelRoutingRule(rule, "rewrite", out);
|
|
419
|
+
}
|
|
420
|
+
for (const rule of config.headers) {
|
|
421
|
+
convertVercelHeaderRule(rule, out);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (config.platform === "netlify") {
|
|
425
|
+
// Netlify expresses rewrites as 200-status redirects; both arrive in
|
|
426
|
+
// `redirects`, already in (almost) `_redirects` syntax.
|
|
427
|
+
for (const rule of config.redirects) {
|
|
428
|
+
convertNetlifyRedirect(rule, out);
|
|
429
|
+
}
|
|
430
|
+
for (const rule of config.headers) {
|
|
431
|
+
convertNetlifyHeaderRule(rule, out);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return out;
|
|
436
|
+
}
|
|
219
437
|
function parsePackageJson(value) {
|
|
220
438
|
const parsed = parseJsonObject(value);
|
|
221
439
|
if (!parsed) {
|
|
@@ -243,50 +461,219 @@ function packageManagerForFiles(files) {
|
|
|
243
461
|
return "yarn";
|
|
244
462
|
return "npm";
|
|
245
463
|
}
|
|
464
|
+
const FRAMEWORKS = [
|
|
465
|
+
{
|
|
466
|
+
// Nextra is Next.js underneath (its docs deploy via `next build` +
|
|
467
|
+
// static export to `out`), so it must supersede the bare `next` row.
|
|
468
|
+
preset: "nextra",
|
|
469
|
+
dependency: "nextra",
|
|
470
|
+
outputDirectory: "out",
|
|
471
|
+
defaultBuildCommand: "next build",
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
preset: "next",
|
|
475
|
+
dependency: "next",
|
|
476
|
+
outputDirectory: "out",
|
|
477
|
+
defaultBuildCommand: "next build",
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
preset: "tanstack-start",
|
|
481
|
+
dependency: "@tanstack/react-start",
|
|
482
|
+
outputDirectory: "dist/client",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
// Nuxt 3 `nuxt generate` pre-renders the site into .output/public
|
|
486
|
+
// (dist/ is just a compatibility symlink to it).
|
|
487
|
+
preset: "nuxt",
|
|
488
|
+
dependency: "nuxt",
|
|
489
|
+
outputDirectory: ".output/public",
|
|
490
|
+
defaultBuildCommand: "nuxt generate",
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
preset: "astro",
|
|
494
|
+
dependency: "astro",
|
|
495
|
+
configFiles: [
|
|
496
|
+
"astro.config.mjs",
|
|
497
|
+
"astro.config.js",
|
|
498
|
+
"astro.config.ts",
|
|
499
|
+
"astro.config.cjs",
|
|
500
|
+
"astro.config.mts",
|
|
501
|
+
"astro.config.cts",
|
|
502
|
+
],
|
|
503
|
+
outputDirectory: "dist",
|
|
504
|
+
defaultBuildCommand: "astro build",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
// VitePress scaffolds into ./docs and builds to <srcDir>/.vitepress/dist.
|
|
508
|
+
// Vite-based, so it must come before the bare `vite` row.
|
|
509
|
+
preset: "vitepress",
|
|
510
|
+
dependency: "vitepress",
|
|
511
|
+
outputDirectory: "docs/.vitepress/dist",
|
|
512
|
+
defaultBuildCommand: "vitepress build docs",
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
preset: "sveltekit",
|
|
516
|
+
dependency: "@sveltejs/kit",
|
|
517
|
+
outputDirectory: "build",
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
// SolidStart builds through vinxi; static assets land in .output/public.
|
|
521
|
+
preset: "solid-start",
|
|
522
|
+
dependency: "@solidjs/start",
|
|
523
|
+
outputDirectory: ".output/public",
|
|
524
|
+
defaultBuildCommand: "vinxi build",
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
// Qwik City starters build with the repo's `build` script (`qwik build`
|
|
528
|
+
// orchestrates the package.json build.* scripts, so there is no
|
|
529
|
+
// meaningful bare-binary default); static output lands in dist.
|
|
530
|
+
preset: "qwik",
|
|
531
|
+
dependency: "@builder.io/qwik-city",
|
|
532
|
+
outputDirectory: "dist",
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
preset: "gatsby",
|
|
536
|
+
dependency: "gatsby",
|
|
537
|
+
outputDirectory: "public",
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
preset: "remix",
|
|
541
|
+
dependency: "@remix-run/dev",
|
|
542
|
+
outputDirectory: "build/client",
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
preset: "docusaurus",
|
|
546
|
+
dependency: "@docusaurus/core",
|
|
547
|
+
outputDirectory: "build",
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
// RedwoodJS: `yarn rw build web` emits the static web side to web/dist.
|
|
551
|
+
preset: "redwoodjs",
|
|
552
|
+
dependency: "@redwoodjs/core",
|
|
553
|
+
configFiles: ["redwood.toml"],
|
|
554
|
+
outputDirectory: "web/dist",
|
|
555
|
+
defaultBuildCommand: "redwood build web",
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
// Angular CLI: `ng build` emits under dist/ (dist/<project>/browser on
|
|
559
|
+
// modern application builds; the runtime candidate scan finds the leaf).
|
|
560
|
+
preset: "angular",
|
|
561
|
+
dependency: "@angular/cli",
|
|
562
|
+
configFiles: ["angular.json"],
|
|
563
|
+
outputDirectory: "dist",
|
|
564
|
+
defaultBuildCommand: "ng build",
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
preset: "ember",
|
|
568
|
+
dependency: "ember-cli",
|
|
569
|
+
configFiles: ["ember-cli-build.js"],
|
|
570
|
+
outputDirectory: "dist",
|
|
571
|
+
defaultBuildCommand: "ember build",
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
preset: "gridsome",
|
|
575
|
+
dependency: "gridsome",
|
|
576
|
+
configFiles: ["gridsome.config.js"],
|
|
577
|
+
outputDirectory: "dist",
|
|
578
|
+
defaultBuildCommand: "gridsome build",
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
preset: "eleventy",
|
|
582
|
+
dependency: "@11ty/eleventy",
|
|
583
|
+
configFiles: [
|
|
584
|
+
".eleventy.js",
|
|
585
|
+
"eleventy.config.js",
|
|
586
|
+
"eleventy.config.mjs",
|
|
587
|
+
"eleventy.config.cjs",
|
|
588
|
+
],
|
|
589
|
+
outputDirectory: "_site",
|
|
590
|
+
defaultBuildCommand: "eleventy",
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
// Hexo is npm-managed, so the dependency is the signal; its _config.yml
|
|
594
|
+
// deliberately stays out of configFiles to avoid clashing with jekyll.
|
|
595
|
+
preset: "hexo",
|
|
596
|
+
dependency: "hexo",
|
|
597
|
+
outputDirectory: "public",
|
|
598
|
+
defaultBuildCommand: "hexo generate",
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
preset: "vite",
|
|
602
|
+
dependency: "vite",
|
|
603
|
+
configFiles: [
|
|
604
|
+
"vite.config.js",
|
|
605
|
+
"vite.config.ts",
|
|
606
|
+
"vite.config.mjs",
|
|
607
|
+
"vite.config.mts",
|
|
608
|
+
"vite.config.cjs",
|
|
609
|
+
"vite.config.cts",
|
|
610
|
+
],
|
|
611
|
+
outputDirectory: "dist",
|
|
612
|
+
defaultBuildCommand: "vite build",
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
// Zola shares config.toml with legacy Hugo sites; a Zola site always has
|
|
616
|
+
// a templates/ directory, which legacy Hugo sites (layouts/) do not.
|
|
617
|
+
preset: "zola",
|
|
618
|
+
configFiles: ["config.toml"],
|
|
619
|
+
requiredFiles: ["templates"],
|
|
620
|
+
outputDirectory: "public",
|
|
621
|
+
defaultBuildCommand: "zola build",
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
preset: "hugo",
|
|
625
|
+
configFiles: ["hugo.toml", "config.toml"],
|
|
626
|
+
outputDirectory: "public",
|
|
627
|
+
defaultBuildCommand: "hugo",
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
preset: "jekyll",
|
|
631
|
+
configFiles: ["_config.yml", "_config.yaml"],
|
|
632
|
+
outputDirectory: "_site",
|
|
633
|
+
defaultBuildCommand: "bundle exec jekyll build",
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
preset: "mkdocs",
|
|
637
|
+
configFiles: ["mkdocs.yml", "mkdocs.yaml"],
|
|
638
|
+
outputDirectory: "site",
|
|
639
|
+
defaultBuildCommand: "mkdocs build",
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
// sphinx-quickstart default layout: conf.py at the root, no separate
|
|
643
|
+
// source dir, `sphinx-build -M html . _build` renders to _build/html.
|
|
644
|
+
preset: "sphinx",
|
|
645
|
+
configFiles: ["conf.py"],
|
|
646
|
+
outputDirectory: "_build/html",
|
|
647
|
+
defaultBuildCommand: "sphinx-build -M html . _build",
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
preset: "middleman",
|
|
651
|
+
configFiles: ["config.rb"],
|
|
652
|
+
requiredFiles: ["source"],
|
|
653
|
+
outputDirectory: "build",
|
|
654
|
+
defaultBuildCommand: "bundle exec middleman build",
|
|
655
|
+
},
|
|
656
|
+
];
|
|
657
|
+
function frameworkForPreset(frameworkPreset) {
|
|
658
|
+
return FRAMEWORKS.find((framework) => framework.preset === frameworkPreset) ?? null;
|
|
659
|
+
}
|
|
660
|
+
function hasRequiredFiles(framework, files) {
|
|
661
|
+
return (framework.requiredFiles ?? []).every((file) => files.has(file));
|
|
662
|
+
}
|
|
246
663
|
function detectFrameworkPreset(files, dependencies) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return "astro";
|
|
255
|
-
if ("@sveltejs/kit" in dependencies)
|
|
256
|
-
return "sveltekit";
|
|
257
|
-
if ("gatsby" in dependencies)
|
|
258
|
-
return "gatsby";
|
|
259
|
-
if ("@remix-run/dev" in dependencies)
|
|
260
|
-
return "remix";
|
|
261
|
-
if ("@docusaurus/core" in dependencies)
|
|
262
|
-
return "docusaurus";
|
|
263
|
-
if (files.has("hugo.toml") || files.has("config.toml"))
|
|
264
|
-
return "hugo";
|
|
265
|
-
if (files.has("_config.yml") || files.has("_config.yaml"))
|
|
266
|
-
return "jekyll";
|
|
267
|
-
return null;
|
|
664
|
+
const byDependency = FRAMEWORKS.find((framework) => framework.dependency !== undefined &&
|
|
665
|
+
framework.dependency in dependencies &&
|
|
666
|
+
hasRequiredFiles(framework, files));
|
|
667
|
+
if (byDependency)
|
|
668
|
+
return byDependency.preset;
|
|
669
|
+
const byConfigFile = FRAMEWORKS.find((framework) => framework.configFiles?.some((file) => files.has(file)) && hasRequiredFiles(framework, files));
|
|
670
|
+
return byConfigFile?.preset ?? null;
|
|
268
671
|
}
|
|
269
672
|
function outputDirectoryForFramework(frameworkPreset, files) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return "dist/client";
|
|
275
|
-
case "gatsby":
|
|
276
|
-
return "public";
|
|
277
|
-
case "sveltekit":
|
|
278
|
-
return "build";
|
|
279
|
-
case "remix":
|
|
280
|
-
return "build/client";
|
|
281
|
-
case "docusaurus":
|
|
282
|
-
return "build";
|
|
283
|
-
case "hugo":
|
|
284
|
-
return "public";
|
|
285
|
-
case "jekyll":
|
|
286
|
-
return "_site";
|
|
287
|
-
default:
|
|
288
|
-
return files.has("dist") ? "dist" : null;
|
|
289
|
-
}
|
|
673
|
+
const framework = frameworkForPreset(frameworkPreset);
|
|
674
|
+
if (framework)
|
|
675
|
+
return framework.outputDirectory;
|
|
676
|
+
return files.has("dist") ? "dist" : null;
|
|
290
677
|
}
|
|
291
678
|
function installCommandForManager(packageManager, packageJson) {
|
|
292
679
|
switch (packageManager) {
|
|
@@ -303,11 +690,7 @@ function installCommandForManager(packageManager, packageJson) {
|
|
|
303
690
|
function buildCommandForDefaults(packageJson, run, frameworkPreset) {
|
|
304
691
|
if (packageJson?.scripts?.build)
|
|
305
692
|
return `${run} build`;
|
|
306
|
-
|
|
307
|
-
return "hugo";
|
|
308
|
-
if (frameworkPreset === "jekyll")
|
|
309
|
-
return "bundle exec jekyll build";
|
|
310
|
-
return null;
|
|
693
|
+
return frameworkForPreset(frameworkPreset)?.defaultBuildCommand ?? null;
|
|
311
694
|
}
|
|
312
695
|
function packageDefaults(files, packageJsonText) {
|
|
313
696
|
const packageJson = parsePackageJson(packageJsonText);
|
|
@@ -1 +1,9 @@
|
|
|
1
|
+
export type ClaimCredential = {
|
|
2
|
+
kind: "token";
|
|
3
|
+
claimToken: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "nonce";
|
|
6
|
+
nonce: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function classifyClaimCredential(raw: string | null | undefined): ClaimCredential | undefined;
|
|
1
9
|
export declare function normalizeClaimToken(token: string | null | undefined): string | undefined;
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
const LEGACY_WORD_TOKEN_SHAPE = /^[a-z]+(?:-[a-z]+){2,}$/;
|
|
2
|
+
export function classifyClaimCredential(raw) {
|
|
3
|
+
const trimmed = raw?.trim();
|
|
4
|
+
if (!trimmed) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
if (trimmed.startsWith("sfc_")) {
|
|
8
|
+
return { kind: "token", claimToken: trimmed };
|
|
9
|
+
}
|
|
10
|
+
const normalized = normalizeClaimToken(trimmed);
|
|
11
|
+
if (normalized && LEGACY_WORD_TOKEN_SHAPE.test(normalized)) {
|
|
12
|
+
return { kind: "token", claimToken: normalized };
|
|
13
|
+
}
|
|
14
|
+
return { kind: "nonce", nonce: trimmed };
|
|
15
|
+
}
|
|
1
16
|
export function normalizeClaimToken(token) {
|
|
2
|
-
const
|
|
3
|
-
|
|
17
|
+
const trimmed = token?.trim();
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (trimmed.startsWith("sfc_")) {
|
|
22
|
+
return trimmed;
|
|
23
|
+
}
|
|
24
|
+
const normalized = trimmed
|
|
4
25
|
.toLowerCase()
|
|
5
26
|
.split(/[\s_-]+/)
|
|
6
27
|
.filter(Boolean)
|
|
@@ -7,4 +7,5 @@
|
|
|
7
7
|
* same content types when uploading and serving version files.
|
|
8
8
|
*/
|
|
9
9
|
export declare function contentTypeForPath(filePath: string): string;
|
|
10
|
+
export declare function textTypeForPath(filePath: string): string | null;
|
|
10
11
|
export declare function contentTypeForBytes(filePath: string, bytes: Uint8Array): string;
|
|
@@ -73,7 +73,7 @@ function hasPrefix(bytes, prefix) {
|
|
|
73
73
|
function hasAsciiPrefix(bytes, prefix) {
|
|
74
74
|
return hasPrefix(bytes, Array.from(prefix, (char) => char.charCodeAt(0)));
|
|
75
75
|
}
|
|
76
|
-
function textTypeForPath(filePath) {
|
|
76
|
+
export function textTypeForPath(filePath) {
|
|
77
77
|
const contentType = contentTypeForPath(filePath);
|
|
78
78
|
const normalized = contentType.split(";")[0]?.trim().toLowerCase();
|
|
79
79
|
return normalized &&
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ApiKeyPreset, type ApiCredentialPolicy, type StatticAction } from "../contracts/api-keys.js";
|
|
2
|
+
type RequestCondition = NonNullable<ApiCredentialPolicy["statements"][number]["conditions"]>;
|
|
3
|
+
export type PolicyCompileInput = {
|
|
4
|
+
tenantId: string;
|
|
5
|
+
teamId: string;
|
|
6
|
+
};
|
|
7
|
+
export type PolicyAuthorizeInput = {
|
|
8
|
+
policy: ApiCredentialPolicy;
|
|
9
|
+
action: StatticAction;
|
|
10
|
+
/**
|
|
11
|
+
* Candidate resource URIs for one request target, evaluated together: spaces
|
|
12
|
+
* are addressed by their canonical site-rooted URI AND a principal-scoped
|
|
13
|
+
* candidate so principal wildcards cover them. A deny matching ANY candidate
|
|
14
|
+
* wins; otherwise an allow matching any candidate permits.
|
|
15
|
+
*/
|
|
16
|
+
resource: string | readonly string[];
|
|
17
|
+
conditions?: RequestCondition;
|
|
18
|
+
};
|
|
19
|
+
export declare const publishActions: ("spaces:read" | "spaces:create" | "spaces:write" | "spaces:publish" | "versions:read" | "versions:promote" | "versions:download" | "builds:read" | "builds:create")[];
|
|
20
|
+
export declare function compileApiKeyPreset(preset: ApiKeyPreset, input: PolicyCompileInput): ApiCredentialPolicy;
|
|
21
|
+
export declare function compileTeamRolePolicy(input: PolicyCompileInput & {
|
|
22
|
+
role: string;
|
|
23
|
+
}): ApiCredentialPolicy;
|
|
24
|
+
export declare function compileSpaceContinuationPolicy(input: {
|
|
25
|
+
tenantId: string;
|
|
26
|
+
spaceId: string;
|
|
27
|
+
}): ApiCredentialPolicy;
|
|
28
|
+
export declare function allowPolicy(actions: StatticAction[], resources: string[]): ApiCredentialPolicy;
|
|
29
|
+
export declare function denyAllPolicy(): ApiCredentialPolicy;
|
|
30
|
+
export declare function policyIsSubsetOfGrantor(requested: ApiCredentialPolicy, grantor: ApiCredentialPolicy): boolean;
|
|
31
|
+
export declare function policyAllows(input: PolicyAuthorizeInput): boolean;
|
|
32
|
+
export declare function grantedTenantsFromPolicy(policy: ApiCredentialPolicy): {
|
|
33
|
+
all: boolean;
|
|
34
|
+
tenantIds: string[];
|
|
35
|
+
};
|
|
36
|
+
export declare function grantedPrincipalsFromPolicy(policy: ApiCredentialPolicy, input: {
|
|
37
|
+
tenantId: string;
|
|
38
|
+
principalType: "team" | "external";
|
|
39
|
+
}): {
|
|
40
|
+
all: boolean;
|
|
41
|
+
principalIds: string[];
|
|
42
|
+
grants: ({
|
|
43
|
+
kind: "principal";
|
|
44
|
+
tenantId: string;
|
|
45
|
+
principalType: "team" | "external";
|
|
46
|
+
principalId: string;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "space";
|
|
49
|
+
tenantId: string;
|
|
50
|
+
spaceId: string;
|
|
51
|
+
})[];
|
|
52
|
+
};
|
|
53
|
+
export declare function policyReaches(policy: ApiCredentialPolicy, resourceBase: string): boolean;
|
|
54
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SPACEFAST_ACCESS_POLICY_VERSION, parseStatticResource, } from "../contracts/api-keys.js";
|
|
2
|
-
const publishActions = [
|
|
2
|
+
export const publishActions = [
|
|
3
3
|
"spaces:read",
|
|
4
4
|
"spaces:create",
|
|
5
5
|
"spaces:write",
|
|
@@ -35,6 +35,9 @@ const domainActions = [
|
|
|
35
35
|
function teamResource(input) {
|
|
36
36
|
return `stt://tenant/${input.tenantId}/principal/team/${input.teamId}/*`;
|
|
37
37
|
}
|
|
38
|
+
function spaceResource(input) {
|
|
39
|
+
return `stt://tenant/${input.tenantId}/space/${input.spaceId}`;
|
|
40
|
+
}
|
|
38
41
|
export function compileApiKeyPreset(preset, input) {
|
|
39
42
|
const resource = teamResource(input);
|
|
40
43
|
switch (preset) {
|
|
@@ -88,6 +91,9 @@ export function compileTeamRolePolicy(input) {
|
|
|
88
91
|
return denyAllPolicy();
|
|
89
92
|
}
|
|
90
93
|
}
|
|
94
|
+
export function compileSpaceContinuationPolicy(input) {
|
|
95
|
+
return allowPolicy(publishActions.filter((action) => action !== "spaces:create"), [spaceResource(input)]);
|
|
96
|
+
}
|
|
91
97
|
export function allowPolicy(actions, resources) {
|
|
92
98
|
return {
|
|
93
99
|
version: SPACEFAST_ACCESS_POLICY_VERSION,
|
|
@@ -145,6 +151,7 @@ export function grantedTenantsFromPolicy(policy) {
|
|
|
145
151
|
// Used to scope list queries; specific actions still go through `policyAllows`.
|
|
146
152
|
export function grantedPrincipalsFromPolicy(policy, input) {
|
|
147
153
|
const principalIds = new Set();
|
|
154
|
+
const spaceIds = new Set();
|
|
148
155
|
let all = false;
|
|
149
156
|
for (const statement of policy.statements) {
|
|
150
157
|
if (statement.effect !== "allow") {
|
|
@@ -169,9 +176,28 @@ export function grantedPrincipalsFromPolicy(policy, input) {
|
|
|
169
176
|
principalIds.add(parsed.principalId);
|
|
170
177
|
}
|
|
171
178
|
}
|
|
179
|
+
else if (parsed.kind === "space") {
|
|
180
|
+
spaceIds.add(parsed.spaceId);
|
|
181
|
+
}
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
|
-
return {
|
|
184
|
+
return {
|
|
185
|
+
all,
|
|
186
|
+
principalIds: [...principalIds],
|
|
187
|
+
grants: [
|
|
188
|
+
...[...principalIds].map((principalId) => ({
|
|
189
|
+
kind: "principal",
|
|
190
|
+
tenantId: input.tenantId,
|
|
191
|
+
principalType: input.principalType,
|
|
192
|
+
principalId,
|
|
193
|
+
})),
|
|
194
|
+
...[...spaceIds].map((spaceId) => ({
|
|
195
|
+
kind: "space",
|
|
196
|
+
tenantId: input.tenantId,
|
|
197
|
+
spaceId,
|
|
198
|
+
})),
|
|
199
|
+
],
|
|
200
|
+
};
|
|
175
201
|
}
|
|
176
202
|
// Whether the policy's allow statements reach a resource subtree at all —
|
|
177
203
|
// either a statement resource covers the base, or it lives inside the base.
|