@rebasepro/server 0.16.1-canary.gef08a6e → 0.17.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.
- package/dist/{admin_block-BeypnEfb.js → admin_block-BX6YULvJ.js} +8 -8
- package/dist/admin_block-BX6YULvJ.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +1 -1
- package/dist/api/rest/idempotency.d.ts +1 -1
- package/dist/{ast-schema-editor-BrarYZCq.js → ast-schema-editor-BLADP9O2.js} +3 -3
- package/dist/ast-schema-editor-BLADP9O2.js.map +1 -0
- package/dist/auth/crypto-utils.d.ts +4 -0
- package/dist/auth/jwt-crypto.d.ts +91 -0
- package/dist/auth/jwt.d.ts +10 -9
- package/dist/auth/middleware.d.ts +1 -1
- package/dist/auth/rate-limiter.d.ts +10 -3
- package/dist/{auth-DU-nUjPp.js → auth-BVPx33qB.js} +49 -76
- package/dist/auth-BVPx33qB.js.map +1 -0
- package/dist/boot/fetch-bundle.d.ts +18 -1
- package/dist/collections/validate-config.d.ts +14 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +248 -66
- package/dist/index.es.js.map +1 -1
- package/dist/{jwt-DoHkMMWF.js → jwt-5VN4C6ln.js} +230 -38
- package/dist/jwt-5VN4C6ln.js.map +1 -0
- package/dist/{schema-editor-routes-Bf5h5Emf.js → schema-editor-routes-BWCIqZoT.js} +2 -2
- package/dist/{schema-editor-routes-Bf5h5Emf.js.map → schema-editor-routes-BWCIqZoT.js.map} +1 -1
- package/dist/src-B-CmIFMr.js.map +1 -1
- package/dist/src-Cdsw7DqV.js.map +1 -1
- package/dist/storage/keys.d.ts +21 -0
- package/dist/utils/ip-address.d.ts +30 -0
- package/dist/utils/portable-crypto.d.ts +71 -0
- package/dist/utils/request-id.d.ts +20 -0
- package/package.json +5 -5
- package/dist/admin_block-BeypnEfb.js.map +0 -1
- package/dist/ast-schema-editor-BrarYZCq.js.map +0 -1
- package/dist/auth-DU-nUjPp.js.map +0 -1
- package/dist/jwt-DoHkMMWF.js.map +0 -1
package/dist/index.es.js
CHANGED
|
@@ -4,11 +4,11 @@ globalThis.process ??= __rebaseProcess;
|
|
|
4
4
|
__rebaseCreateRequire(import.meta.url);
|
|
5
5
|
import { A as GeoPoint, M as RebaseApiError, N as RebaseClientError, O as EntityReference, S as toSnakeCase, _ as fieldKeyForColumn, b as buildCompositeId, c as collectAllPages, d as normalizeOrderBy, f as serializeOrderBy, g as getEffectiveSecurityRules, h as resolveDataSource, j as Vector, k as EntityRelation, l as paginateFind, m as createDataSourceRegistry, n as buildSdkData, o as serializeFilter, p as CollectionRegistry, s as serializeLogicalCondition, t as buildRoutedRebaseData, u as resolveFindWindow, v as findRelation, x as resolvePrimaryKeys, y as resolveCollectionRelations } from "./src-B-CmIFMr.js";
|
|
6
6
|
import { a as RLS_JWT_SQL, b as toCanonicalOp, c as getCollectionDataPath, f as DEFAULT_DATA_SOURCE_KEY, i as buildResourceGraph, n as setTopicRuntime, o as RLS_ROLES_SQL, s as RLS_UID_SQL, t as declaredSubscriptions, u as isPostgresCollectionConfig, v as parseRelationAggregateSort } from "./src-Cdsw7DqV.js";
|
|
7
|
-
import { n as ADMIN_PROPERTY_KEYS, t as ADMIN_COLLECTION_KEYS } from "./admin_block-
|
|
7
|
+
import { n as ADMIN_PROPERTY_KEYS, t as ADMIN_COLLECTION_KEYS } from "./admin_block-BX6YULvJ.js";
|
|
8
8
|
import { a as isDuplicateObjectRace, i as isConcurrentDdlRace, n as createDdlBootstrapper, o as isSQLAdmin, s as isSchemaEditingAdmin, t as CONCURRENT_DDL_SQLSTATES } from "./ddl-bootstrap-5YZCZ8qk.js";
|
|
9
|
-
import {
|
|
9
|
+
import { A as findStorageSuffixCollision, C as canonicalStorageKey, M as storageEnvSuffix, O as sha256Hex, S as canonicalStorageId, b as InvalidStorageKeyError, c as getJwks, d as hasAsymmetricSigningKey, i as generateDownloadToken, j as normalizeStorageSources, k as DEFAULT_STORAGE_SOURCE_KEY, n as configureJwt, p as isJwtConfigured, v as normalizePemFromEnv, x as canonicalStorageBucket, y as InvalidStorageBucketError } from "./jwt-5VN4C6ln.js";
|
|
10
10
|
import { n as createContractRoutes, r as computeSchemaVersion } from "./contract-routes-D8TvxPLo.js";
|
|
11
|
-
import { $ as html, A as activeDevEmailSink, B as hashPassword, C as providerVerifiedEmail, Ct as PUBLIC_STORAGE_PREFIX, D as createDataRateLimiter, E as DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT, F as SMTPEmailService, G as getEmailVerificationTemplate, H as verifyPassword, I as createEmailService, J as getUserInvitationTemplate, K as getMagicLinkTemplate, L as assertEmailLinkBases, M as createDevEmailSink, N as extractLinks, O as defaultAuthLimiter, P as registerDevEmailSink, Q as escapeHtml, R as resolveEmailLinkBase, S as pkceTokenParams, St as isOperationAllowed, T as createBuiltinAuthAdapter, U as generateSecurePassword, V as validatePasswordStrength, W as getEmailOtpTemplate, X as resolveEmailBranding, Y as getWelcomeEmailTemplate, Z as RawHtml, _ as verifyOidcIdToken, _t as safeCompare, a as resolveRateLimitStoreKind, at as fileTokenAuth, b as createGoogleProvider, bt as hasAdministrativeRole, c as createSlackProvider, ct as queryTokenAuth, d as createDiscordProvider, dt as createApiKeyPreAuth, et as raw, f as createTwitterProvider, ft as createFunctionApiKeyGuard, g as tryVerifyOidcIdToken, gt as extractBearerToken, h as createMicrosoftProvider, ht as validateApiKey, i as createApiKeyStore, it as extractUserFromToken, j as clearActiveDevEmailSink, k as MemoryRateLimitStore, l as createBitbucketProvider, lt as requireAdmin, m as createAppleProvider, mt as isApiKeyToken, n as createCustomAuthAdapter, nt as createAuthMiddleware, o as createSqlRateLimitStore, ot as optionalAuth, p as createFacebookProvider, pt as createStorageApiKeyGuard, q as getPasswordResetTemplate, r as createApiKeyRoutes, rt as createRequireAuth, s as createSpotifyProvider, st as publicObjectAuth, tt as createAdapterAuthMiddleware, u as createGitLabProvider, ut as requireAuth, v as createGitHubProvider, vt as SERVICE_IDENTITY, w as createJwksRoutes, wt as isPublicStoragePath, x as oauthCodeFlowSchema, xt as httpMethodToOperation, y as createLinkedinProvider, yt as scopeDataDriver, z as resolveAuthHooks } from "./auth-
|
|
11
|
+
import { $ as html, A as activeDevEmailSink, B as hashPassword, C as providerVerifiedEmail, Ct as PUBLIC_STORAGE_PREFIX, D as createDataRateLimiter, E as DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT, F as SMTPEmailService, G as getEmailVerificationTemplate, H as verifyPassword, I as createEmailService, J as getUserInvitationTemplate, K as getMagicLinkTemplate, L as assertEmailLinkBases, M as createDevEmailSink, N as extractLinks, O as defaultAuthLimiter, P as registerDevEmailSink, Q as escapeHtml, R as resolveEmailLinkBase, S as pkceTokenParams, St as isOperationAllowed, T as createBuiltinAuthAdapter, U as generateSecurePassword, V as validatePasswordStrength, W as getEmailOtpTemplate, X as resolveEmailBranding, Y as getWelcomeEmailTemplate, Z as RawHtml, _ as verifyOidcIdToken, _t as safeCompare, a as resolveRateLimitStoreKind, at as fileTokenAuth, b as createGoogleProvider, bt as hasAdministrativeRole, c as createSlackProvider, ct as queryTokenAuth, d as createDiscordProvider, dt as createApiKeyPreAuth, et as raw, f as createTwitterProvider, ft as createFunctionApiKeyGuard, g as tryVerifyOidcIdToken, gt as extractBearerToken, h as createMicrosoftProvider, ht as validateApiKey, i as createApiKeyStore, it as extractUserFromToken, j as clearActiveDevEmailSink, k as MemoryRateLimitStore, l as createBitbucketProvider, lt as requireAdmin, m as createAppleProvider, mt as isApiKeyToken, n as createCustomAuthAdapter, nt as createAuthMiddleware, o as createSqlRateLimitStore, ot as optionalAuth, p as createFacebookProvider, pt as createStorageApiKeyGuard, q as getPasswordResetTemplate, r as createApiKeyRoutes, rt as createRequireAuth, s as createSpotifyProvider, st as publicObjectAuth, tt as createAdapterAuthMiddleware, u as createGitLabProvider, ut as requireAuth, v as createGitHubProvider, vt as SERVICE_IDENTITY, w as createJwksRoutes, wt as isPublicStoragePath, x as oauthCodeFlowSchema, xt as httpMethodToOperation, y as createLinkedinProvider, yt as scopeDataDriver, z as resolveAuthHooks } from "./auth-BVPx33qB.js";
|
|
12
12
|
import { t as revokeInternalTableSql } from "./internal-tables-DpxfPaEB.js";
|
|
13
13
|
import { r as hostEnv, t as logger } from "./logger-DS03e908.js";
|
|
14
14
|
import { n as errorHandler, t as ApiError } from "./errors-DBwpj9N8.js";
|
|
@@ -30,7 +30,7 @@ import { t as createCronStore } from "./cron-store-CbFfQhbg.js";
|
|
|
30
30
|
import { a as parseBackupTimestamp, i as parseBackupDestination, n as createBackupRoutes, o as readBackupBytes, r as listBackupObjects } from "./backup-CRdZkA6c.js";
|
|
31
31
|
import { i as createJobStore, n as createJobQueue, r as defaultBackoff } from "./jobs-B2ELModo.js";
|
|
32
32
|
import { t as METRIC_SERIES } from "./history-store-oiqhb3HU.js";
|
|
33
|
-
import { createHash, createSign, randomBytes
|
|
33
|
+
import { createHash, createSign, randomBytes } from "node:crypto";
|
|
34
34
|
import * as fs$3 from "fs";
|
|
35
35
|
import fs, { existsSync } from "fs";
|
|
36
36
|
import * as path$3 from "path";
|
|
@@ -39,9 +39,7 @@ import { pathToFileURL } from "url";
|
|
|
39
39
|
import { Hono } from "hono";
|
|
40
40
|
import { promisify } from "util";
|
|
41
41
|
import * as crypto$1 from "crypto";
|
|
42
|
-
import { createHmac, randomUUID
|
|
43
|
-
import * as path$2 from "node:path";
|
|
44
|
-
import path$1 from "node:path";
|
|
42
|
+
import { createHmac, randomUUID } from "crypto";
|
|
45
43
|
import { bodyLimit } from "hono/body-limit";
|
|
46
44
|
import { csrf } from "hono/csrf";
|
|
47
45
|
import { compress } from "hono/compress";
|
|
@@ -50,12 +48,12 @@ import * as fs$2 from "node:fs/promises";
|
|
|
50
48
|
import fsp from "node:fs/promises";
|
|
51
49
|
import os from "node:os";
|
|
52
50
|
import { mkdir, open, unlink, writeFile } from "fs/promises";
|
|
51
|
+
import * as path$2 from "node:path";
|
|
52
|
+
import path$1 from "node:path";
|
|
53
53
|
import { execFile } from "node:child_process";
|
|
54
54
|
import { promisify as promisify$1 } from "node:util";
|
|
55
55
|
import { env, getRuntimeKey } from "hono/adapter";
|
|
56
56
|
import { sql } from "drizzle-orm";
|
|
57
|
-
import { lookup } from "node:dns/promises";
|
|
58
|
-
import { isIP } from "node:net";
|
|
59
57
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
60
58
|
import { createServer } from "http";
|
|
61
59
|
import { cors } from "hono/cors";
|
|
@@ -136,6 +134,7 @@ var COLLECTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
136
134
|
"securityRules",
|
|
137
135
|
"schema",
|
|
138
136
|
"search",
|
|
137
|
+
"indexes",
|
|
139
138
|
"path",
|
|
140
139
|
"subcollections",
|
|
141
140
|
"admin"
|
|
@@ -325,22 +324,39 @@ var ProblemCollector = class {
|
|
|
325
324
|
constructor(unknownKeys) {
|
|
326
325
|
this.unknownKeys = unknownKeys;
|
|
327
326
|
}
|
|
328
|
-
error(path, message) {
|
|
327
|
+
error(path, message, kind = "incoherent") {
|
|
329
328
|
this.problems.push({
|
|
330
329
|
severity: "error",
|
|
330
|
+
kind,
|
|
331
|
+
path,
|
|
332
|
+
message
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Config that parses but cannot do what it says.
|
|
337
|
+
*
|
|
338
|
+
* Not gated on `unknownKeys`: that policy is about keys this version has
|
|
339
|
+
* never heard of, where silence is defensible. This is the opposite —
|
|
340
|
+
* every key is recognised, and the combination is known to be wrong.
|
|
341
|
+
*/
|
|
342
|
+
warn(path, message) {
|
|
343
|
+
this.problems.push({
|
|
344
|
+
severity: "warning",
|
|
345
|
+
kind: "incoherent",
|
|
331
346
|
path,
|
|
332
347
|
message
|
|
333
348
|
});
|
|
334
349
|
}
|
|
335
350
|
/** A key we know moved or died. Always fatal — we know exactly what to do. */
|
|
336
351
|
migrated(path, key, migration) {
|
|
337
|
-
this.error(path, `\`${key}\` is no longer read here. ${migration.fix}.` + (migration.codemod ? ` Run \`${migration.codemod}\` to migrate the whole project.` : ""));
|
|
352
|
+
this.error(path, `\`${key}\` is no longer read here. ${migration.fix}.` + (migration.codemod ? ` Run \`${migration.codemod}\` to migrate the whole project.` : ""), "key");
|
|
338
353
|
}
|
|
339
354
|
/** A key nobody recognises. Might be metadata, might be newer than us. */
|
|
340
355
|
unknown(path, key, context) {
|
|
341
356
|
if (this.unknownKeys === "off") return;
|
|
342
357
|
this.problems.push({
|
|
343
358
|
severity: this.unknownKeys === "error" ? "error" : "warning",
|
|
359
|
+
kind: "key",
|
|
344
360
|
path,
|
|
345
361
|
message: `\`${key}\` is not a known ${context} key and is being ignored. If it is deliberate metadata this is safe; if it is a typo or a key from an older version, the feature it configures is silently absent.`
|
|
346
362
|
});
|
|
@@ -433,6 +449,40 @@ function checkProperty(property, path, collect) {
|
|
|
433
449
|
function checkProperties(properties, path, collect) {
|
|
434
450
|
for (const [key, property] of Object.entries(properties)) checkProperty(property, `${path}.${key}`, collect);
|
|
435
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* A board's two halves, checked against each other and against `properties`.
|
|
454
|
+
*
|
|
455
|
+
* Every failure here parses cleanly, boots, serves rows and renders a board.
|
|
456
|
+
* The only symptom is that dragging does not stick — which no test, no
|
|
457
|
+
* typecheck and no config review catches, because the config is *valid*, just
|
|
458
|
+
* incoherent. The panel says so in an amber bar, but only to whoever opens the
|
|
459
|
+
* board; this says it to whoever starts the server.
|
|
460
|
+
*
|
|
461
|
+
* The order key is a `fractional-indexing` key in the base36, lower-case
|
|
462
|
+
* alphabet (`"i0"`, `"i1"`, `"i0i"`) — a string, always. Postgres does the
|
|
463
|
+
* sorting and its default collation is not byte ordering, which is why the
|
|
464
|
+
* alphabet is single-case; see `useKanbanDragAndDrop.ts` in the admin.
|
|
465
|
+
*/
|
|
466
|
+
function checkBoardConfig(collection, at, collect) {
|
|
467
|
+
const admin = isPlainObject$1(collection.admin) ? collection.admin : void 0;
|
|
468
|
+
if (!admin) return;
|
|
469
|
+
const orderProperty = admin.orderProperty;
|
|
470
|
+
if ((isPlainObject$1(admin.kanban) || Array.isArray(admin.enabledViews) && admin.enabledViews.includes("kanban") || admin.defaultViewMode === "kanban") && orderProperty === void 0) collect.warn(`${at}.admin.orderProperty`, "this collection renders a Kanban board but declares no `orderProperty`, so a card's position within a column has nowhere to be stored and resets on the next read. (Moving a card *between* columns still works — that writes `kanban.columnProperty`.) Add a hidden string property and name it here: `__order: { name: \"Order\", type: \"string\", admin: { disabled: true, hideFromCollection: true } }`.");
|
|
471
|
+
if (orderProperty === void 0) return;
|
|
472
|
+
if (typeof orderProperty !== "string" || !orderProperty) {
|
|
473
|
+
collect.error(`${at}.admin.orderProperty`, "`orderProperty` must be the key of a property in this collection.");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (!isPlainObject$1(collection.properties)) return;
|
|
477
|
+
const target = collection.properties[orderProperty];
|
|
478
|
+
if (target === void 0) {
|
|
479
|
+
collect.error(`${at}.admin.orderProperty`, `\`orderProperty: "${orderProperty}"\` names no property in this collection. Nothing reads or writes it, so the board cannot be reordered at all.`);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (typeof target === "function") return;
|
|
483
|
+
if (!isPlainObject$1(target)) return;
|
|
484
|
+
if (target.type !== "string") collect.error(`${at}.properties.${orderProperty}`, `\`orderProperty\` names a \`${String(target.type)}\` property, and an order key is a string — a \`fractional-indexing\` key such as "i0" or "i0i", not an index. Stored values can never be valid, so the board asks to be initialised forever and the initialisation itself fails writing a string into this column. Change it to \`type: "string"\`.`);
|
|
485
|
+
}
|
|
436
486
|
function checkCollection(collection, index, collect) {
|
|
437
487
|
if (!isPlainObject$1(collection)) {
|
|
438
488
|
collect.error(`collection[${index}]`, "a collection must be an object.");
|
|
@@ -455,6 +505,7 @@ function checkCollection(collection, index, collect) {
|
|
|
455
505
|
}
|
|
456
506
|
if (isPlainObject$1(collection.properties)) checkProperties(collection.properties, `${at}.properties`, collect);
|
|
457
507
|
else if (collection.properties !== void 0) collect.error(`${at}.properties`, "`properties` must be an object keyed by property name.");
|
|
508
|
+
checkBoardConfig(collection, at, collect);
|
|
458
509
|
if (Array.isArray(collection.relations)) collection.relations.forEach((relation, i) => {
|
|
459
510
|
const name = isPlainObject$1(relation) && typeof relation.relationName === "string" ? relation.relationName : String(i);
|
|
460
511
|
checkRelation(relation, `${at}.relations[${name}]`, collect);
|
|
@@ -487,11 +538,15 @@ function assertCollectionConfigs(collections, options = {}) {
|
|
|
487
538
|
if (problems.length === 0) return;
|
|
488
539
|
const warnings = problems.filter((p) => p.severity === "warning");
|
|
489
540
|
const errors = problems.filter((p) => p.severity === "error");
|
|
490
|
-
|
|
541
|
+
const unknownWarnings = warnings.filter((p) => p.kind === "key");
|
|
542
|
+
const incoherentWarnings = warnings.filter((p) => p.kind === "incoherent");
|
|
543
|
+
if (unknownWarnings.length > 0) logger.warn(`[collections] ${unknownWarnings.length} unrecognised key(s) in the collection config, ignored:\n\n` + render(unknownWarnings) + "\n\nSet REBASE_STRICT_COLLECTION_CONFIG=error to make these fail the boot.\n");
|
|
544
|
+
if (incoherentWarnings.length > 0) logger.warn(`[collections] ${incoherentWarnings.length} collection(s) configured for something they cannot do:\n\n` + render(incoherentWarnings) + "\n");
|
|
491
545
|
if (errors.length === 0) return;
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
546
|
+
const deadKeys = errors.filter((p) => p.kind === "key");
|
|
547
|
+
const incoherent = errors.filter((p) => p.kind === "incoherent");
|
|
548
|
+
const sections = [deadKeys.length > 0 ? "These keys are not read by this version. Nothing would have failed at runtime — whatever they configure would simply be absent — so they are fatal at boot instead.\n\n" + render(deadKeys) : void 0, incoherent.length > 0 ? "These parse, and cannot do what they say. Nothing would have failed at runtime — the feature would simply not work — so they are fatal at boot instead.\n\n" + render(incoherent) : void 0].filter(Boolean);
|
|
549
|
+
throw new Error(`${errors.length} problem(s) in the collection config.\n\n` + sections.join("\n\n") + "\n");
|
|
495
550
|
}
|
|
496
551
|
//#endregion
|
|
497
552
|
//#region src/collections/loader.ts
|
|
@@ -1670,7 +1725,7 @@ function stableStringify(value) {
|
|
|
1670
1725
|
* correction.
|
|
1671
1726
|
*/
|
|
1672
1727
|
function requestFingerprint(method, path, body) {
|
|
1673
|
-
return
|
|
1728
|
+
return sha256Hex(`${method.toUpperCase()} ${path}\n${stableStringify(body)}`);
|
|
1674
1729
|
}
|
|
1675
1730
|
/**
|
|
1676
1731
|
* Returns `undefined` when the driver cannot run SQL, which disables the whole
|
|
@@ -2046,7 +2101,7 @@ var RestApiGenerator = class {
|
|
|
2046
2101
|
const idempotencyKey = c.req.header(IDEMPOTENCY_HEADER);
|
|
2047
2102
|
const uid = c.get("user")?.uid;
|
|
2048
2103
|
const store = this.idempotency();
|
|
2049
|
-
const claimed = idempotencyKey && store ? await store.claim(idempotencyKey, uid, requestFingerprint(c.req.method, c.req.path, body)) : void 0;
|
|
2104
|
+
const claimed = idempotencyKey && store ? await store.claim(idempotencyKey, uid, await requestFingerprint(c.req.method, c.req.path, body)) : void 0;
|
|
2050
2105
|
if (claimed?.status === "replay") return respond(claimed.response);
|
|
2051
2106
|
if (claimed?.status === "mismatch") throw idempotencyKeyReused(idempotencyKey);
|
|
2052
2107
|
if (claimed?.status === "in-flight") throw ApiError.conflict(`A request with Idempotency-Key '${idempotencyKey}' is already in progress. Retry once it has answered; its result will be replayed.`, "IDEMPOTENCY_KEY_IN_PROGRESS");
|
|
@@ -2200,7 +2255,7 @@ var RestApiGenerator = class {
|
|
|
2200
2255
|
const idempotencyKey = c.req.header(IDEMPOTENCY_HEADER);
|
|
2201
2256
|
const uid = c.get("user")?.uid;
|
|
2202
2257
|
const store = this.idempotency();
|
|
2203
|
-
const claimed = idempotencyKey && store ? await store.claim(idempotencyKey, uid, requestFingerprint(c.req.method, c.req.path, body)) : void 0;
|
|
2258
|
+
const claimed = idempotencyKey && store ? await store.claim(idempotencyKey, uid, await requestFingerprint(c.req.method, c.req.path, body)) : void 0;
|
|
2204
2259
|
if (claimed?.status === "replay") return c.json(claimed.response, 201);
|
|
2205
2260
|
if (claimed?.status === "mismatch") throw idempotencyKeyReused(idempotencyKey);
|
|
2206
2261
|
if (claimed?.status === "in-flight") throw ApiError.conflict(`A request with Idempotency-Key '${idempotencyKey}' is already in progress. Retry once it has answered; its result will be replayed.`, "IDEMPOTENCY_KEY_IN_PROGRESS");
|
|
@@ -2255,14 +2310,33 @@ var RestApiGenerator = class {
|
|
|
2255
2310
|
* gateway in front of this API would reject partial updates the server
|
|
2256
2311
|
* would have accepted.
|
|
2257
2312
|
*
|
|
2258
|
-
* PUT
|
|
2259
|
-
*
|
|
2260
|
-
*
|
|
2261
|
-
*
|
|
2262
|
-
*
|
|
2263
|
-
*
|
|
2313
|
+
* PUT is mounted on the same handler, and is **not** in the generated
|
|
2314
|
+
* spec: the contract is PATCH, and a client generated from the spec can
|
|
2315
|
+
* only ever send it. The alias exists because dropping it was shipped
|
|
2316
|
+
* ahead of the clients that needed it — every published SDK up to and
|
|
2317
|
+
* including 0.16.0 sends PUT from `collection.update()`, so a control
|
|
2318
|
+
* plane that had already dropped the alias answered `rebase cloud
|
|
2319
|
+
* stop | start | restart` with a 404 naming the caller's own collection.
|
|
2320
|
+
* A verb removal is only safe once the oldest client in the field speaks
|
|
2321
|
+
* the new one.
|
|
2322
|
+
*
|
|
2323
|
+
* Delete it when no released SDK sends PUT any more — i.e. one release
|
|
2324
|
+
* after the first published client whose `update()` sends PATCH, giving
|
|
2325
|
+
* the field a version to move to.
|
|
2326
|
+
*
|
|
2327
|
+
* `Deprecation: true` is RFC 8594's signal, and how a caller finds out
|
|
2328
|
+
* before that happens. No `Sunset` date: the removal is gated on which
|
|
2329
|
+
* SDKs are in the field rather than on a calendar, and inventing a date
|
|
2330
|
+
* we would not honour is worse than saying only what is true. Set inline
|
|
2331
|
+
* rather than through a wrapper — a wrapper has to name the handler's
|
|
2332
|
+
* return type, and Hono's is worth neither widening nor casting for one
|
|
2333
|
+
* header at two call sites.
|
|
2264
2334
|
*/
|
|
2265
2335
|
this.router.patch(`${basePath}/:id`, updateEntity);
|
|
2336
|
+
this.router.put(`${basePath}/:id`, (c) => {
|
|
2337
|
+
c.header("Deprecation", "true");
|
|
2338
|
+
return updateEntity(c);
|
|
2339
|
+
});
|
|
2266
2340
|
this.router.delete(`${basePath}/:id`, async (c) => {
|
|
2267
2341
|
this.enforceApiKeyPermission(c, collection.slug);
|
|
2268
2342
|
const id = c.req.param("id");
|
|
@@ -2432,6 +2506,10 @@ var RestApiGenerator = class {
|
|
|
2432
2506
|
return c.json(response);
|
|
2433
2507
|
};
|
|
2434
2508
|
this.router.patch("/:parent/:parentId/:rest{.+}", updateNested);
|
|
2509
|
+
this.router.put("/:parent/:parentId/:rest{.+}", (c, next) => {
|
|
2510
|
+
c.header("Deprecation", "true");
|
|
2511
|
+
return updateNested(c, next);
|
|
2512
|
+
});
|
|
2435
2513
|
this.router.delete("/:parent/:parentId/:rest{.+}", async (c, next) => {
|
|
2436
2514
|
const rest = c.req.param("rest");
|
|
2437
2515
|
if (!rest || rest === "undefined") return next();
|
|
@@ -2576,32 +2654,12 @@ function responseCompression() {
|
|
|
2576
2654
|
}
|
|
2577
2655
|
//#endregion
|
|
2578
2656
|
//#region src/utils/request-id.ts
|
|
2579
|
-
/**
|
|
2580
|
-
* X-Request-ID Middleware for Hono.
|
|
2581
|
-
*
|
|
2582
|
-
* Generates a unique request identifier (UUID v4) for every inbound
|
|
2583
|
-
* request, or propagates an existing `X-Request-ID` header from the
|
|
2584
|
-
* caller. The ID is:
|
|
2585
|
-
*
|
|
2586
|
-
* 1. Stored in the Hono context (`c.get("requestId")`)
|
|
2587
|
-
* 2. Echoed back on the response as `X-Request-ID`
|
|
2588
|
-
*
|
|
2589
|
-
* Downstream middleware and handlers (request logger, error handler,
|
|
2590
|
-
* etc.) read the ID from context to include it in log entries and
|
|
2591
|
-
* error responses, enabling end-to-end request tracing across services.
|
|
2592
|
-
*
|
|
2593
|
-
* @example
|
|
2594
|
-
* ```ts
|
|
2595
|
-
* import { requestId } from "@rebasepro/server";
|
|
2596
|
-
* app.use("/*", requestId());
|
|
2597
|
-
* ```
|
|
2598
|
-
*/
|
|
2599
2657
|
var REQUEST_ID_HEADER = "X-Request-ID";
|
|
2600
2658
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2601
2659
|
function requestId() {
|
|
2602
2660
|
return async (c, next) => {
|
|
2603
2661
|
const incoming = c.req.header(REQUEST_ID_HEADER);
|
|
2604
|
-
const id = incoming && UUID_RE.test(incoming) ? incoming : randomUUID();
|
|
2662
|
+
const id = incoming && UUID_RE.test(incoming) ? incoming : crypto.randomUUID();
|
|
2605
2663
|
c.set("requestId", id);
|
|
2606
2664
|
await next();
|
|
2607
2665
|
c.header(REQUEST_ID_HEADER, id);
|
|
@@ -3775,7 +3833,7 @@ var TusHandler = class {
|
|
|
3775
3833
|
if (Number.isNaN(uploadLength) || uploadLength <= 0) throw ApiError.badRequest("Invalid Upload-Length");
|
|
3776
3834
|
if (uploadLength > MAX_UPLOAD_SIZE) throw new ApiError(413, "PAYLOAD_TOO_LARGE", `Upload-Length exceeds maximum of ${MAX_UPLOAD_SIZE} bytes`);
|
|
3777
3835
|
const metadata = this.parseMetadata(c.req.header("Upload-Metadata") || "");
|
|
3778
|
-
const id = randomUUID
|
|
3836
|
+
const id = randomUUID();
|
|
3779
3837
|
const rawKey = metadata.key || metadata.filename || "";
|
|
3780
3838
|
let key;
|
|
3781
3839
|
try {
|
|
@@ -4567,7 +4625,7 @@ function createStorageRoutes(config) {
|
|
|
4567
4625
|
const scopedPath = `${bucket}/${resolvedPath}`;
|
|
4568
4626
|
if (isPublicStoragePath(scopedPath)) downloadConfig.metadata.public = true;
|
|
4569
4627
|
else {
|
|
4570
|
-
downloadConfig.metadata.token = generateDownloadToken(scopedPath, 300, storageId);
|
|
4628
|
+
downloadConfig.metadata.token = await generateDownloadToken(scopedPath, 300, storageId);
|
|
4571
4629
|
downloadConfig.metadata.tokenExpiresIn = 300;
|
|
4572
4630
|
}
|
|
4573
4631
|
}
|
|
@@ -4865,6 +4923,8 @@ function resolveStorageAccessControl(config) {
|
|
|
4865
4923
|
}
|
|
4866
4924
|
//#endregion
|
|
4867
4925
|
//#region src/init/docs.ts
|
|
4926
|
+
/** Bound once the generator is imported; the route closes over it. */
|
|
4927
|
+
var generateOpenApiSpecFn = null;
|
|
4868
4928
|
/**
|
|
4869
4929
|
* Serve the OpenAPI document, and Swagger UI outside production.
|
|
4870
4930
|
*
|
|
@@ -4875,15 +4935,16 @@ function resolveStorageAccessControl(config) {
|
|
|
4875
4935
|
* paths that 404, with a Try-It button next to each.
|
|
4876
4936
|
*/
|
|
4877
4937
|
async function mountOpenApiDocs(app, basePath, enableSwagger, serverCollections, requireAuth) {
|
|
4878
|
-
if (
|
|
4938
|
+
if (serverCollections.length === 0) return;
|
|
4939
|
+
const isPublic = enableSwagger !== false;
|
|
4940
|
+
const spec = (c) => c.json(generateOpenApiSpecFn(serverCollections, {
|
|
4941
|
+
basePath,
|
|
4942
|
+
requireAuth
|
|
4943
|
+
}));
|
|
4879
4944
|
const { generateOpenApiSpec } = await import("./openapi-generator-FFGT0H6O.js");
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
requireAuth
|
|
4884
|
-
});
|
|
4885
|
-
return c.json(spec);
|
|
4886
|
-
});
|
|
4945
|
+
generateOpenApiSpecFn = generateOpenApiSpec;
|
|
4946
|
+
if (isPublic) app.get(`${basePath}/docs`, (c) => spec(c));
|
|
4947
|
+
else app.get(`${basePath}/docs`, createRequireAuth({}), requireAdmin, (c) => spec(c));
|
|
4887
4948
|
if (process.env.NODE_ENV !== "production") {
|
|
4888
4949
|
app.get(`${basePath}/swagger`, (c) => {
|
|
4889
4950
|
return c.html(`<!DOCTYPE html>
|
|
@@ -12977,7 +13038,7 @@ async function _initializeRebaseBackend(config) {
|
|
|
12977
13038
|
]) {
|
|
12978
13039
|
const providerConfig = safeAuthConfig[key];
|
|
12979
13040
|
if (providerConfig && requiredFields.every((f) => Boolean(providerConfig[f]))) {
|
|
12980
|
-
const createFn = (await import("./auth-
|
|
13041
|
+
const createFn = (await import("./auth-BVPx33qB.js").then((n) => n.t))[factory];
|
|
12981
13042
|
oauthProviders.push(createFn(providerConfig));
|
|
12982
13043
|
}
|
|
12983
13044
|
}
|
|
@@ -13079,7 +13140,7 @@ async function _initializeRebaseBackend(config) {
|
|
|
13079
13140
|
let schemaEditorOff = schemaEditorUnavailable();
|
|
13080
13141
|
let schemaEditorRoutes;
|
|
13081
13142
|
if (!schemaEditorOff && config.collectionsDir) try {
|
|
13082
|
-
schemaEditorRoutes = (await import("./schema-editor-routes-
|
|
13143
|
+
schemaEditorRoutes = (await import("./schema-editor-routes-BWCIqZoT.js")).createSchemaEditorRoutes(config.collectionsDir);
|
|
13083
13144
|
} catch (err) {
|
|
13084
13145
|
if (err?.code === "ERR_MODULE_NOT_FOUND") {
|
|
13085
13146
|
schemaEditorOff = {
|
|
@@ -13145,7 +13206,7 @@ async function _initializeRebaseBackend(config) {
|
|
|
13145
13206
|
},
|
|
13146
13207
|
writeSource: schemaEditorRoutes ? async (change) => {
|
|
13147
13208
|
const applyEdit = async (dir, collectionId, collection) => {
|
|
13148
|
-
const { AstSchemaEditor } = await import("./ast-schema-editor-
|
|
13209
|
+
const { AstSchemaEditor } = await import("./ast-schema-editor-BLADP9O2.js");
|
|
13149
13210
|
await new AstSchemaEditor(dir).saveCollection(collectionId, collection, { partial: false });
|
|
13150
13211
|
};
|
|
13151
13212
|
if (remoteRepo) {
|
|
@@ -14307,6 +14368,55 @@ function loadEnv(options) {
|
|
|
14307
14368
|
return env;
|
|
14308
14369
|
}
|
|
14309
14370
|
//#endregion
|
|
14371
|
+
//#region src/utils/ip-address.ts
|
|
14372
|
+
/**
|
|
14373
|
+
* `net.isIP`, without `node:net`.
|
|
14374
|
+
*
|
|
14375
|
+
* The SSRF guard in `services/outbound-url-guard.ts` needs to know whether a
|
|
14376
|
+
* host is already a literal address before it decides whether to resolve it.
|
|
14377
|
+
* That is pure string parsing — it opens no socket and asks nothing of the
|
|
14378
|
+
* platform — but importing it from `node:net` pinned the guard to a Node
|
|
14379
|
+
* process anyway. The guard's only other host dependency, the resolver, is
|
|
14380
|
+
* already injectable, so this is what stood between it and running anywhere.
|
|
14381
|
+
*
|
|
14382
|
+
* That matters more here than the line count suggests. On a host with no
|
|
14383
|
+
* `dns.lookup` the guard has to be given a resolver (DNS-over-HTTPS, or the
|
|
14384
|
+
* platform's own), and a module that cannot be *imported* there cannot be
|
|
14385
|
+
* given anything. The alternative — an isolate deployment quietly running
|
|
14386
|
+
* without the guard — is how a webhook destination becomes a way to read the
|
|
14387
|
+
* metadata endpoint.
|
|
14388
|
+
*
|
|
14389
|
+
* The grammar is Node's own, transcribed from `lib/internal/net.js`, and
|
|
14390
|
+
* `test/property/ip-address.property.test.ts` holds it to that by comparing against
|
|
14391
|
+
* `net.isIP` over generated addresses. Transcribing a validator by eye and
|
|
14392
|
+
* declaring it equivalent is not a thing to do to a security boundary; being
|
|
14393
|
+
* able to *check* the claim on every run is what makes it acceptable.
|
|
14394
|
+
*
|
|
14395
|
+
* @module
|
|
14396
|
+
*/
|
|
14397
|
+
var IPV4_SEGMENT = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
|
|
14398
|
+
var IPV4 = `(?:${IPV4_SEGMENT}\\.){3}${IPV4_SEGMENT}`;
|
|
14399
|
+
var IPV4_RE = new RegExp(`^${IPV4}$`);
|
|
14400
|
+
var IPV6_SEGMENT = "(?:[0-9a-fA-F]{1,4})";
|
|
14401
|
+
/**
|
|
14402
|
+
* One alternative per position the `::` elision can occupy, which is why this
|
|
14403
|
+
* is nine branches rather than something shorter and wrong: a compact pattern
|
|
14404
|
+
* that admits two elisions, or a trailing dotted quad in the wrong place,
|
|
14405
|
+
* accepts strings the resolver would reject and the guard would then judge on
|
|
14406
|
+
* bytes it invented.
|
|
14407
|
+
*/
|
|
14408
|
+
var IPV6 = `(?:(?:${IPV6_SEGMENT}:){7}(?:${IPV6_SEGMENT}|:)|(?:${IPV6_SEGMENT}:){6}(?:${IPV4}|:${IPV6_SEGMENT}|:)|(?:${IPV6_SEGMENT}:){5}(?::${IPV4}|(?::${IPV6_SEGMENT}){1,2}|:)|(?:${IPV6_SEGMENT}:){4}(?:(?::${IPV6_SEGMENT}){0,1}:${IPV4}|(?::${IPV6_SEGMENT}){1,3}|:)|(?:${IPV6_SEGMENT}:){3}(?:(?::${IPV6_SEGMENT}){0,2}:${IPV4}|(?::${IPV6_SEGMENT}){1,4}|:)|(?:${IPV6_SEGMENT}:){2}(?:(?::${IPV6_SEGMENT}){0,3}:${IPV4}|(?::${IPV6_SEGMENT}){1,5}|:)|(?:${IPV6_SEGMENT}:){1}(?:(?::${IPV6_SEGMENT}){0,4}:${IPV4}|(?::${IPV6_SEGMENT}){1,6}|:)|(?::(?:(?::${IPV6_SEGMENT}){0,5}:${IPV4}|(?::${IPV6_SEGMENT}){1,7}|:)))(?:%[0-9a-zA-Z-.:]{1,})?`;
|
|
14409
|
+
var IPV6_RE = new RegExp(`^${IPV6}$`);
|
|
14410
|
+
/**
|
|
14411
|
+
* `4`, `6`, or `0` for anything that is not an IP address — the same three
|
|
14412
|
+
* answers, for the same inputs, as `net.isIP`.
|
|
14413
|
+
*/
|
|
14414
|
+
function ipVersion(address) {
|
|
14415
|
+
if (IPV4_RE.test(address)) return 4;
|
|
14416
|
+
if (IPV6_RE.test(address)) return 6;
|
|
14417
|
+
return 0;
|
|
14418
|
+
}
|
|
14419
|
+
//#endregion
|
|
14310
14420
|
//#region src/services/outbound-url-guard.ts
|
|
14311
14421
|
/**
|
|
14312
14422
|
* Refusal to send a request to a destination the guard does not allow.
|
|
@@ -14328,8 +14438,27 @@ var BLOCKED_HOST_SUFFIXES = [
|
|
|
14328
14438
|
".internal",
|
|
14329
14439
|
".home.arpa"
|
|
14330
14440
|
];
|
|
14441
|
+
/**
|
|
14442
|
+
* The resolver when the caller supplies none: Node's.
|
|
14443
|
+
*
|
|
14444
|
+
* Loaded on use rather than imported at the top, which is what keeps this
|
|
14445
|
+
* module — the check standing between a stored URL and the pod's own metadata
|
|
14446
|
+
* endpoint — loadable on a runtime that has no `node:dns`. There the caller
|
|
14447
|
+
* passes {@link OutboundUrlGuardOptions.lookup} instead, pointing at whatever
|
|
14448
|
+
* the host resolves names with.
|
|
14449
|
+
*
|
|
14450
|
+
* A host with neither fails **closed**, and says which of the two it is
|
|
14451
|
+
* missing: the alternative to resolving a name is not "allow it", it is "do
|
|
14452
|
+
* not send".
|
|
14453
|
+
*/
|
|
14331
14454
|
async function defaultLookup(hostname) {
|
|
14332
|
-
|
|
14455
|
+
let dns;
|
|
14456
|
+
try {
|
|
14457
|
+
dns = await import("node:dns/promises");
|
|
14458
|
+
} catch {
|
|
14459
|
+
throw new BlockedUrlError(`Cannot check where "${hostname}" points: this runtime has no \`node:dns\`, and no \`lookup\` was supplied to the outbound URL guard. Pass one — the guard cannot be skipped, because skipping it is what makes a stored URL a way to reach the internal network.`);
|
|
14460
|
+
}
|
|
14461
|
+
return (await dns.lookup(hostname, {
|
|
14333
14462
|
all: true,
|
|
14334
14463
|
verbatim: true
|
|
14335
14464
|
})).map((e) => e.address);
|
|
@@ -14348,7 +14477,7 @@ function parseIpv6(address) {
|
|
|
14348
14477
|
const lastColon = text.lastIndexOf(":");
|
|
14349
14478
|
const maybeV4 = text.slice(lastColon + 1);
|
|
14350
14479
|
if (maybeV4.includes(".")) {
|
|
14351
|
-
if (
|
|
14480
|
+
if (ipVersion(maybeV4) !== 4) return null;
|
|
14352
14481
|
tail = maybeV4.split(".").map(Number);
|
|
14353
14482
|
text = text.slice(0, lastColon);
|
|
14354
14483
|
if (text.endsWith(":")) text += ":";
|
|
@@ -14396,7 +14525,7 @@ function blockedIpv4Reason(bytes) {
|
|
|
14396
14525
|
* outbound call whose destination comes from data.
|
|
14397
14526
|
*/
|
|
14398
14527
|
function blockedAddressReason(address) {
|
|
14399
|
-
const version =
|
|
14528
|
+
const version = ipVersion(address);
|
|
14400
14529
|
if (version === 4) return blockedIpv4Reason(address.split(".").map(Number));
|
|
14401
14530
|
if (version !== 6) return `not an IP address: ${address}`;
|
|
14402
14531
|
const bytes = parseIpv6(address);
|
|
@@ -14447,7 +14576,7 @@ async function assertAllowedOutboundUrl(rawUrl, options = {}) {
|
|
|
14447
14576
|
const hostname = url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname;
|
|
14448
14577
|
const lowered = hostname.toLowerCase();
|
|
14449
14578
|
if (lowered === "localhost" || BLOCKED_HOST_SUFFIXES.some((suffix) => lowered.endsWith(suffix))) throw new BlockedUrlError(`Webhook URL host "${hostname}" is an internal name`);
|
|
14450
|
-
const addresses =
|
|
14579
|
+
const addresses = ipVersion(hostname) ? [hostname] : await (options.lookup ?? defaultLookup)(hostname);
|
|
14451
14580
|
if (addresses.length === 0) throw new BlockedUrlError(`Webhook URL host "${hostname}" resolved to no addresses`);
|
|
14452
14581
|
for (const address of addresses) {
|
|
14453
14582
|
const reason = blockedAddressReason(address);
|
|
@@ -14636,7 +14765,7 @@ var WebhookDispatcher = class {
|
|
|
14636
14765
|
"Content-Type": "application/json",
|
|
14637
14766
|
"X-Webhook-Id": webhook.id,
|
|
14638
14767
|
"X-Webhook-Event": event,
|
|
14639
|
-
"X-Webhook-Delivery": randomUUID
|
|
14768
|
+
"X-Webhook-Delivery": randomUUID(),
|
|
14640
14769
|
"X-Webhook-Attempt": String(attemptNumber),
|
|
14641
14770
|
...webhook.headers || {}
|
|
14642
14771
|
};
|
|
@@ -16780,6 +16909,29 @@ var BUNDLE_URL_ENV = "REBASE_BUNDLE_URL";
|
|
|
16780
16909
|
/** Bearer token for that fetch. Does not expire — see the module note. */
|
|
16781
16910
|
var BUNDLE_TOKEN_ENV = "REBASE_BUNDLE_TOKEN";
|
|
16782
16911
|
/**
|
|
16912
|
+
* Records which URL produced the tree sitting in the fetch directory.
|
|
16913
|
+
*
|
|
16914
|
+
* Reusing an already-unpacked bundle is what makes a restart cheap, but "there
|
|
16915
|
+
* is a bundle here" is not the same question as "is it the bundle we were told
|
|
16916
|
+
* to run". Anything that fills the directory behind the runtime's back — an
|
|
16917
|
+
* orphaned `fetch-bundle` init container left on a Deployment, a stale volume —
|
|
16918
|
+
* used to win over the URL, and the pod then served whatever it found while
|
|
16919
|
+
* every deploy reported success. The URL is the instruction; this is how a
|
|
16920
|
+
* restart tells whether the disk already satisfies it.
|
|
16921
|
+
*
|
|
16922
|
+
* `infra/docker/entrypoint.mjs` reads this file by the same name to decide
|
|
16923
|
+
* whether to fetch at all — keep the two in step.
|
|
16924
|
+
*/
|
|
16925
|
+
var BUNDLE_SOURCE_FILENAME = ".rebase-bundle-source";
|
|
16926
|
+
/** What the tree in `destination` was fetched from, or null if unknown. */
|
|
16927
|
+
function unpackedBundleSource(destination) {
|
|
16928
|
+
try {
|
|
16929
|
+
return fs$1.readFileSync(path$1.join(destination, BUNDLE_SOURCE_FILENAME), "utf8").trim();
|
|
16930
|
+
} catch {
|
|
16931
|
+
return null;
|
|
16932
|
+
}
|
|
16933
|
+
}
|
|
16934
|
+
/**
|
|
16783
16935
|
* Whether this process should fetch its bundle rather than read one from disk.
|
|
16784
16936
|
*
|
|
16785
16937
|
* An explicit `REBASE_BUNDLE` — a path — always wins. A platform that mounted a
|
|
@@ -16789,14 +16941,28 @@ var BUNDLE_TOKEN_ENV = "REBASE_BUNDLE_TOKEN";
|
|
|
16789
16941
|
function shouldFetchBundle(env = process.env) {
|
|
16790
16942
|
return Boolean(env["REBASE_BUNDLE_URL"]) && !env.REBASE_BUNDLE;
|
|
16791
16943
|
}
|
|
16944
|
+
/**
|
|
16945
|
+
* Whether the system `tar` is GNU tar.
|
|
16946
|
+
*
|
|
16947
|
+
* Asked rather than assumed, because the answer differs between a developer's
|
|
16948
|
+
* machine and the Linux image the same code runs in, and the flags below are
|
|
16949
|
+
* GNU spellings that bsdtar rejects outright. Probed once per process.
|
|
16950
|
+
*/
|
|
16951
|
+
var gnuTar;
|
|
16952
|
+
function isGnuTar() {
|
|
16953
|
+
gnuTar ??= run("tar", ["--version"]).then(({ stdout }) => /GNU tar/.test(String(stdout))).catch(() => false);
|
|
16954
|
+
return gnuTar;
|
|
16955
|
+
}
|
|
16792
16956
|
/** Untar with the system `tar`, which every base image has. */
|
|
16793
16957
|
async function extractWithTar(tarball, destination) {
|
|
16794
|
-
|
|
16958
|
+
const args = [
|
|
16795
16959
|
"-xzmf",
|
|
16796
16960
|
tarball,
|
|
16797
16961
|
"-C",
|
|
16798
16962
|
destination
|
|
16799
|
-
]
|
|
16963
|
+
];
|
|
16964
|
+
if (await isGnuTar()) args.push("--no-same-owner", "--no-same-permissions", "--no-overwrite-dir");
|
|
16965
|
+
await run("tar", args);
|
|
16800
16966
|
}
|
|
16801
16967
|
/**
|
|
16802
16968
|
* Download and unpack a bundle, returning the directory it landed in.
|
|
@@ -16999,6 +17165,21 @@ async function fetchBundle(options) {
|
|
|
16999
17165
|
fs$1.rmSync(tarball, { force: true });
|
|
17000
17166
|
throw new Error(`Could not download the bundle from ${options.url}: ` + (lastError instanceof Error ? lastError.message : String(lastError)));
|
|
17001
17167
|
}
|
|
17168
|
+
const previous = unpackedBundleSource(destination);
|
|
17169
|
+
if (previous !== null && previous !== options.url) {
|
|
17170
|
+
logger.info("Discarding a bundle unpacked from a different source", {
|
|
17171
|
+
previous,
|
|
17172
|
+
replacing: options.url
|
|
17173
|
+
});
|
|
17174
|
+
for (const entry of fs$1.readdirSync(destination)) {
|
|
17175
|
+
const victim = path$1.join(destination, entry);
|
|
17176
|
+
if (victim === tarball) continue;
|
|
17177
|
+
fs$1.rmSync(victim, {
|
|
17178
|
+
recursive: true,
|
|
17179
|
+
force: true
|
|
17180
|
+
});
|
|
17181
|
+
}
|
|
17182
|
+
}
|
|
17002
17183
|
const { size } = fs$1.statSync(tarball);
|
|
17003
17184
|
try {
|
|
17004
17185
|
await extract(tarball, destination);
|
|
@@ -17009,6 +17190,7 @@ async function fetchBundle(options) {
|
|
|
17009
17190
|
}
|
|
17010
17191
|
const root = bundleRootIn(destination);
|
|
17011
17192
|
if (!root) throw new Error(`The bundle downloaded from ${options.url} unpacked without a ${MANIFEST_FILENAME}. It is not a Rebase bundle, or it was truncated.`);
|
|
17193
|
+
fs$1.writeFileSync(path$1.join(destination, BUNDLE_SOURCE_FILENAME), options.url);
|
|
17012
17194
|
if (options.installDependencies ?? true) await (options.installImpl ?? installBundleDependencies)(root);
|
|
17013
17195
|
return root;
|
|
17014
17196
|
}
|
|
@@ -17578,6 +17760,6 @@ var RUNTIME_STARTUP_BUDGET_SECONDS = 300;
|
|
|
17578
17760
|
/** Where a bundle is mounted, and what the runtime is told to read. */
|
|
17579
17761
|
var RUNTIME_BUNDLE_MOUNT = "/bundle";
|
|
17580
17762
|
//#endregion
|
|
17581
|
-
export { ApiError, BlockedUrlError, BundleError, CONCURRENT_DDL_SQLSTATES, CronScheduler, DEFAULT_DRIVER_ID, DEFAULT_MAX_FILE_SIZE, DEFAULT_STORAGE_ID, DOCUMENT_MIME_TYPES, DefaultDriverRegistry, DefaultStorageRegistry, GCSStorageController, IMAGE_MIME_TYPES, LocalStorageController, MetricsRegistry, RUNTIME_BUNDLE_MOUNT, RUNTIME_HEALTH_PATH, RUNTIME_LIVENESS_PATH, RUNTIME_MIN_TERMINATION_GRACE_SECONDS, RUNTIME_PRESTOP_DRAIN_SECONDS, RUNTIME_PROBE_PATHS, RUNTIME_STARTUP_BUDGET_SECONDS, RawHtml, S3StorageController, SMTPEmailService, StoragePolicyError, TOPOLOGY_ENV_VARS, TransformCache, TusHandler, UnknownStorageSourceError, WEBHOOK_DELIVERY_TASK, WebhookDispatcher, _resetRebaseMock, _setRebaseMock, activeDevEmailSink, applyCollectionDefaults, assertAllowedOutboundUrl, assertCollectionConfigs, assertDistinctSuffixes, assertEmailLinkBases, assertWriteRequestValid, authJwt, authRoles, authUid, blockedAddressReason, bootFromBundle, classifySurface, cleanupDevPortFile, clearActiveDevEmailSink, compileStoragePolicies, createAppleProvider, createBackupRoutes, createBitbucketProvider, createBuiltinAuthAdapter, createContractRoutes, createCronRoutes, createCronStore, createCustomAuthAdapter, createDdlBootstrapper, createDevEmailSink, createDiscordProvider, createEmailService, createFacebookProvider, createFunctionRoutes, createGitHubProvider, createGitLabProvider, createGoogleProvider, createHistoryRoutes, createJobQueue, createJobStore, createJwksRoutes, createLinkedinProvider, createMetricsMiddleware, createMetricsRoutes, createMicrosoftProvider, createRlsAudit, createSlackProvider, createSourceBundle, createSpotifyProvider, createStorageController, createStorageRoutes, createTwitterProvider, defaultBackoff, defineCron, defineFunction, drainBackgroundWork, envSuffixForKey, errorHandler, escapeHtml, extractLinks, extractUserFromToken, fileTokenAuth, findCollectionConfigProblems, generateSecurePassword, getApiKey, getDriver, getEmailOtpTemplate, getEmailVerificationTemplate, getEnv, getJwks, getMagicLinkTemplate, getPasswordResetTemplate, getRequestId, getRoles, getUser, getUserId, getUserInvitationTemplate, getWelcomeEmailTemplate, hasAsymmetricSigningKey, hasRole, hashPassword, html, httpMethodToOperation, identityResolved, initializeDataSource, initializeDataSources, initializeRebaseBackend, installShutdownHandlers, isAdmin, isApiKeyToken, isAuthAdapter, isAuthenticated, isConcurrentDdlRace, isDatabaseAdapter, isDuplicateObjectRace, isLocalhostOrigin, isNodeRuntime, isOperationAllowed, isTopologyEnvVar, isTransformableImage, lazyResource, listBackupObjects, listenWithPortRetry, loadBootEnv, loadBundle, loadBundleConfigExports, loadBundleSchema, loadCollectionsFromDirectory, loadCronJobsFromDirectory, loadCronJobsWithDiagnostics, loadDeclaredStorageSources, loadEnv, loadFunctionsFromDirectory, loadFunctionsWithDiagnostics, loadUsersCollection, logger, oauthCodeFlowSchema, optionalAuth, parseBackupDestination, parseBackupTimestamp, parseTransformOptions, pendingBackgroundWork, pkceTokenParams, providerVerifiedEmail, queryTokenAuth, raw, readBackupBytes, readBundleManifest, rebase, registerDevEmailSink, requireAdmin, requireAuth, requireDriver, requireEnv, requireRole, resolveAuthHooks, resolveAuthOptions, resolveCorsOrigin, resolveDataSources, resolveEmailBranding, resolveEmailLinkBase, resolveEmailOptions, resolveRequireAuth, resolveStorageAccessControl, resolveStorageBackend, resolveStorageSources, runFromBundle, runtimeKey, safeCompare, serveSPA, summarize as summarizeRlsScan, transformImage, tryVerifyOidcIdToken, unknownKeyPolicyFromEnv, validateApiKey, validateCronExpression, validatePasswordStrength, verifyOidcIdToken, verifyPassword, waitUntil };
|
|
17763
|
+
export { ApiError, BlockedUrlError, BundleError, CONCURRENT_DDL_SQLSTATES, CronScheduler, DEFAULT_DRIVER_ID, DEFAULT_MAX_FILE_SIZE, DEFAULT_STORAGE_ID, DOCUMENT_MIME_TYPES, DefaultDriverRegistry, DefaultStorageRegistry, GCSStorageController, IMAGE_MIME_TYPES, LocalStorageController, MetricsRegistry, RUNTIME_BUNDLE_MOUNT, RUNTIME_HEALTH_PATH, RUNTIME_LIVENESS_PATH, RUNTIME_MIN_TERMINATION_GRACE_SECONDS, RUNTIME_PRESTOP_DRAIN_SECONDS, RUNTIME_PROBE_PATHS, RUNTIME_STARTUP_BUDGET_SECONDS, RawHtml, S3StorageController, SMTPEmailService, StoragePolicyError, TOPOLOGY_ENV_VARS, TransformCache, TusHandler, UnknownStorageSourceError, WEBHOOK_DELIVERY_TASK, WebhookDispatcher, _resetRebaseMock, _setRebaseMock, activeDevEmailSink, applyCollectionDefaults, assertAllowedOutboundUrl, assertCollectionConfigs, assertDistinctSuffixes, assertEmailLinkBases, assertWriteRequestValid, authJwt, authRoles, authUid, blockedAddressReason, bootFromBundle, classifySurface, cleanupDevPortFile, clearActiveDevEmailSink, compileStoragePolicies, configureJwt, createAppleProvider, createBackupRoutes, createBitbucketProvider, createBuiltinAuthAdapter, createContractRoutes, createCronRoutes, createCronStore, createCustomAuthAdapter, createDdlBootstrapper, createDevEmailSink, createDiscordProvider, createEmailService, createFacebookProvider, createFunctionRoutes, createGitHubProvider, createGitLabProvider, createGoogleProvider, createHistoryRoutes, createJobQueue, createJobStore, createJwksRoutes, createLinkedinProvider, createMetricsMiddleware, createMetricsRoutes, createMicrosoftProvider, createRlsAudit, createSlackProvider, createSourceBundle, createSpotifyProvider, createStorageController, createStorageRoutes, createTwitterProvider, defaultBackoff, defineCron, defineFunction, drainBackgroundWork, envSuffixForKey, errorHandler, escapeHtml, extractLinks, extractUserFromToken, fileTokenAuth, findCollectionConfigProblems, generateSecurePassword, getApiKey, getDriver, getEmailOtpTemplate, getEmailVerificationTemplate, getEnv, getJwks, getMagicLinkTemplate, getPasswordResetTemplate, getRequestId, getRoles, getUser, getUserId, getUserInvitationTemplate, getWelcomeEmailTemplate, hasAsymmetricSigningKey, hasRole, hashPassword, html, httpMethodToOperation, identityResolved, initializeDataSource, initializeDataSources, initializeRebaseBackend, installShutdownHandlers, isAdmin, isApiKeyToken, isAuthAdapter, isAuthenticated, isConcurrentDdlRace, isDatabaseAdapter, isDuplicateObjectRace, isLocalhostOrigin, isNodeRuntime, isOperationAllowed, isTopologyEnvVar, isTransformableImage, lazyResource, listBackupObjects, listenWithPortRetry, loadBootEnv, loadBundle, loadBundleConfigExports, loadBundleSchema, loadCollectionsFromDirectory, loadCronJobsFromDirectory, loadCronJobsWithDiagnostics, loadDeclaredStorageSources, loadEnv, loadFunctionsFromDirectory, loadFunctionsWithDiagnostics, loadUsersCollection, logger, oauthCodeFlowSchema, optionalAuth, parseBackupDestination, parseBackupTimestamp, parseTransformOptions, pendingBackgroundWork, pkceTokenParams, providerVerifiedEmail, queryTokenAuth, raw, readBackupBytes, readBundleManifest, rebase, registerDevEmailSink, requireAdmin, requireAuth, requireDriver, requireEnv, requireRole, resolveAuthHooks, resolveAuthOptions, resolveCorsOrigin, resolveDataSources, resolveEmailBranding, resolveEmailLinkBase, resolveEmailOptions, resolveRequireAuth, resolveStorageAccessControl, resolveStorageBackend, resolveStorageSources, runFromBundle, runtimeKey, safeCompare, serveSPA, summarize as summarizeRlsScan, transformImage, tryVerifyOidcIdToken, unknownKeyPolicyFromEnv, validateApiKey, validateCronExpression, validatePasswordStrength, verifyOidcIdToken, verifyPassword, waitUntil };
|
|
17582
17764
|
|
|
17583
17765
|
//# sourceMappingURL=index.es.js.map
|