@rebasepro/server 0.10.1-canary.d8d45b2 → 0.10.1-canary.ff9ccd6

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.
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Move presentation keys into the `admin` block.
3
+ *
4
+ * `ADMIN_COLLECTION_KEYS` comes from `@rebasepro/types` rather than being spelled
5
+ * out here, so adding a field to `AdminCollectionOptions` cannot leave this writer
6
+ * behind. Any such key already inside `admin` wins over a top-level copy: the
7
+ * nested one is what the file said, and a flat duplicate is the view model's
8
+ * flattening leaking back.
9
+ */
10
+ export declare function nestAdminKeys(collectionData: Record<string, unknown>): Record<string, unknown>;
1
11
  export declare class AstSchemaEditor {
2
12
  private project;
3
13
  private collectionsDir;
package/dist/index.es.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "module";
2
2
  import process from "process";
3
3
  __createRequire(import.meta.url);
4
4
  import { _ as __toESM, a as generateRefreshToken, c as getRefreshTokenTtlMs, d as verifyAccessToken, f as verifyDownloadToken, g as __require, h as __exportAll, i as generateDownloadToken, l as hashRefreshToken, m as __commonJSMin, n as configureJwt, o as getAccessTokenExpiry, p as require_jsonwebtoken, r as generateAccessToken, s as getRefreshTokenExpiry, t as MAX_COOKIE_AGE_MS } from "./jwt-Dj7r7QX7.js";
5
- import { _ as EntityRelation, a as DEFAULT_DATA_SOURCE_KEY, b as RebaseApiError, c as policy, d as isPostgresCollectionConfig, f as CANONICAL_TO_REST, g as EntityReference, h as toCanonicalOp, i as DEFAULT_STORAGE_SOURCE_KEY, l as getCollectionDataPath, m as REST_TO_CANONICAL, n as serializeCollections, o as getDataSourceCapabilities, p as NULL_OPS, r as SCHEMA_VERSION_HEADER, s as isSQLAdmin, t as computeSchemaVersion, u as getDeclaredSubcollections, v as GeoPoint, x as RebaseClientError, y as Vector } from "./src-BITicbgD.js";
5
+ import { S as RebaseClientError, _ as EntityReference, a as DEFAULT_DATA_SOURCE_KEY, b as Vector, c as policy, d as isPostgresCollectionConfig, g as toCanonicalOp, h as REST_TO_CANONICAL, i as DEFAULT_STORAGE_SOURCE_KEY, l as getCollectionDataPath, m as NULL_OPS, n as serializeCollections, o as getDataSourceCapabilities, p as CANONICAL_TO_REST, r as SCHEMA_VERSION_HEADER, s as isSQLAdmin, t as computeSchemaVersion, u as getDeclaredSubcollections, v as EntityRelation, x as RebaseApiError, y as GeoPoint } from "./src-DRUswG_w.js";
6
6
  import { t as logger } from "./logger-BYU66ENZ.js";
7
7
  import { t as nativeDynamicImport } from "./dynamic-import-Dvh-K5fl.js";
8
8
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
@@ -1689,39 +1689,9 @@ policy.or(policy.serverContext(), policy.rolesOverlap(["admin"]));
1689
1689
  return jsonLogic;
1690
1690
  });
1691
1691
  })))();
1692
- //#endregion
1693
- //#region ../common/src/util/filter-operator-resolution.ts
1694
- /**
1695
- * Default operators offered per property type, before engine capabilities and
1696
- * per-property narrowing are applied. These mirror what the built-in filter
1697
- * fields can render.
1698
- */
1699
- var COMPARISON_OPS = [
1700
- "==",
1701
- "!=",
1702
- ">",
1703
- ">=",
1704
- "<",
1705
- "<="
1706
- ];
1707
- var NULL_CHECK_OPS = ["is-null", "is-not-null"];
1708
- var MEMBERSHIP_OPS = ["in", "not-in"];
1709
- var PATTERN_OPS = [
1710
- "like",
1711
- "ilike",
1712
- "not-like",
1713
- "not-ilike"
1714
- ];
1715
- [
1716
- ...COMPARISON_OPS,
1717
- ...MEMBERSHIP_OPS,
1718
- ...PATTERN_OPS,
1719
- ...NULL_CHECK_OPS
1720
- ], [
1721
- ...COMPARISON_OPS,
1722
- ...MEMBERSHIP_OPS,
1723
- ...NULL_CHECK_OPS
1724
- ], [...COMPARISON_OPS, ...NULL_CHECK_OPS], [...NULL_CHECK_OPS], [...MEMBERSHIP_OPS, ...NULL_CHECK_OPS], [...MEMBERSHIP_OPS, ...NULL_CHECK_OPS];
1692
+ /**
1693
+ * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.
1694
+ */
1725
1695
  //#endregion
1726
1696
  //#region ../../node_modules/.pnpm/fast-equals@6.0.0/node_modules/fast-equals/dist/es/index.mjs
1727
1697
  var { getOwnPropertyNames, getOwnPropertySymbols } = Object;
@@ -12888,6 +12858,7 @@ async function verifyGoogleAccessToken(accessToken, expectedClientId) {
12888
12858
  function createGoogleProvider(config) {
12889
12859
  const clientId = typeof config === "string" ? config : config.clientId;
12890
12860
  const clientSecret = typeof config === "string" ? void 0 : config.clientSecret;
12861
+ if (!clientSecret) console.warn("[Rebase] Google provider configured without a client secret. ID-token and access-token sign-in still work, but the authorization-code flow used by the admin login button will fail. Set GOOGLE_CLIENT_SECRET to enable it.");
12891
12862
  let googleClient;
12892
12863
  async function getClient() {
12893
12864
  if (!googleClient) {
@@ -15044,7 +15015,7 @@ function assertStorageAccessControlConfigured(state, isProduction) {
15044
15015
  //#region src/init/docs.ts
15045
15016
  async function mountOpenApiDocs(app, basePath, enableSwagger, activeCollections, requireAuth) {
15046
15017
  if (enableSwagger === false || activeCollections.length === 0) return;
15047
- const { generateOpenApiSpec } = await import("./openapi-generator-DqSIwNLV.js");
15018
+ const { generateOpenApiSpec } = await import("./openapi-generator-C3_pTcsb.js");
15048
15019
  app.get(`${basePath}/docs`, (c) => {
15049
15020
  const spec = generateOpenApiSpec(activeCollections, {
15050
15021
  basePath,
@@ -15546,6 +15517,34 @@ function createAuth(transport, options) {
15546
15517
  transport.setToken(null);
15547
15518
  emit("SIGNED_OUT", null);
15548
15519
  }
15520
+ /**
15521
+ * Recover from a 401 on an ordinary API request.
15522
+ *
15523
+ * Returns `true` when the caller should retry — we minted a fresh access
15524
+ * token. When the refresh is rejected *fatally* (the refresh token itself
15525
+ * is invalid, expired or revoked) this client can no longer act as the
15526
+ * user at all, so we drop the session and emit `SIGNED_OUT`. UIs gate on
15527
+ * that event, so they show their login screen instead of leaving the user
15528
+ * staring at "Invalid or expired token" on every view.
15529
+ *
15530
+ * Transient failures (offline, 5xx, backend restarting) keep the session:
15531
+ * the scheduled refresh backs off and retries, and the token is very
15532
+ * likely still good once the backend answers again.
15533
+ */
15534
+ async function handleUnauthorized() {
15535
+ if (!currentSession) return false;
15536
+ if (authFlowMode !== "cookie" && !currentSession.refreshToken) {
15537
+ abandonSessionLocally();
15538
+ return false;
15539
+ }
15540
+ try {
15541
+ await refreshSession();
15542
+ return true;
15543
+ } catch (err) {
15544
+ if (isFatalRefreshError(err)) abandonSessionLocally();
15545
+ return false;
15546
+ }
15547
+ }
15549
15548
  async function attemptScheduledRefresh(attempt) {
15550
15549
  try {
15551
15550
  await refreshSession();
@@ -16047,6 +16046,7 @@ function createAuth(transport, options) {
16047
16046
  signInWithSpotify,
16048
16047
  signOut,
16049
16048
  refreshSession,
16049
+ handleUnauthorized,
16050
16050
  getUser,
16051
16051
  findUserByEmail,
16052
16052
  updateUser,
@@ -20190,14 +20190,7 @@ function createRebaseClient(options) {
20190
20190
  } catch (e) {}
20191
20191
  return session?.accessToken || options.token || "";
20192
20192
  },
20193
- onUnauthorized: options.onUnauthorized || (async () => {
20194
- try {
20195
- await auth.refreshSession();
20196
- return true;
20197
- } catch (e) {
20198
- return false;
20199
- }
20200
- })
20193
+ onUnauthorized: options.onUnauthorized || (() => auth.handleUnauthorized())
20201
20194
  });
20202
20195
  auth.onAuthStateChange((event, session) => {
20203
20196
  if (!ws) return;
@@ -20207,14 +20200,7 @@ function createRebaseClient(options) {
20207
20200
  }
20208
20201
  });
20209
20202
  }
20210
- if (!options.onUnauthorized) transport.setOnUnauthorized(async () => {
20211
- try {
20212
- await auth.refreshSession();
20213
- return true;
20214
- } catch (e) {
20215
- return false;
20216
- }
20217
- });
20203
+ if (!options.onUnauthorized) transport.setOnUnauthorized(() => auth.handleUnauthorized());
20218
20204
  /**
20219
20205
  * Suggest the closest known collection key for a mistyped accessor.
20220
20206
  * Uses edit-distance-1 and prefix matching — no external dependency.
@@ -20952,7 +20938,7 @@ async function _initializeRebaseBackend(config) {
20952
20938
  if (schemaEditorEnabled && config.collectionsDir) {
20953
20939
  let editorModule;
20954
20940
  try {
20955
- editorModule = await import("./schema-editor-routes-D3ef8zu1.js");
20941
+ editorModule = await import("./schema-editor-routes-CF-g0gVN.js");
20956
20942
  } catch (err) {
20957
20943
  if (err?.code === "ERR_MODULE_NOT_FOUND") logger.warn("Schema Editor disabled: its dependency ts-morph is not installed. Run `npm install ts-morph@28.0.0` to enable it.");
20958
20944
  else throw err;
@@ -22461,8 +22447,11 @@ var DEV_PORT_FILENAME = ".rebase-dev-port";
22461
22447
  * Try to `listen` on `startPort`. If the port is busy (`EADDRINUSE`), increment
22462
22448
  * and retry up to `maxAttempts` times.
22463
22449
  *
22464
- * When a port file already exists (written by a previous run), the saved port
22465
- * is tried first to maintain port affinity across tsx watch restarts.
22450
+ * When a port file written by a previous run exists *and that run asked for the
22451
+ * same `startPort`*, the port it landed on is tried first, so tsx watch restarts
22452
+ * keep the address the frontend was configured with. A different `startPort` means
22453
+ * the configuration changed and the file is ignored — an explicitly requested port
22454
+ * is never overridden by a stale one.
22466
22455
  *
22467
22456
  * Resolves with the port that was actually bound.
22468
22457
  *
@@ -22490,8 +22479,10 @@ function listenWithPortRetry(server, startPort, options) {
22490
22479
  if (portFileDir) try {
22491
22480
  const portFile = path.join(portFileDir, DEV_PORT_FILENAME);
22492
22481
  if (fs.existsSync(portFile)) {
22493
- const saved = parseInt(fs.readFileSync(portFile, "utf-8").trim(), 10);
22494
- if (saved > 0 && saved < 65536 && saved !== startPort) affinityPort = saved;
22482
+ const [savedRaw, requestedRaw] = fs.readFileSync(portFile, "utf-8").trim().split(/\s+/);
22483
+ const saved = parseInt(savedRaw, 10);
22484
+ const requestedThen = requestedRaw === void 0 ? NaN : parseInt(requestedRaw, 10);
22485
+ if (saved > 0 && saved < 65536 && saved !== startPort && (Number.isNaN(requestedThen) || requestedThen === startPort)) affinityPort = saved;
22495
22486
  }
22496
22487
  } catch {}
22497
22488
  return new Promise((resolve, reject) => {
@@ -22509,24 +22500,29 @@ function listenWithPortRetry(server, startPort, options) {
22509
22500
  }
22510
22501
  const port = portsToTry[index];
22511
22502
  attempt++;
22512
- const onError = (err) => {
22513
- if (err.code === "EADDRINUSE") {
22514
- server.removeListener("error", onError);
22515
- tryNext(index + 1);
22516
- } else reject(err);
22517
- };
22518
- server.once("error", onError);
22519
- server.listen(port, host, () => {
22520
- server.removeListener("error", onError);
22503
+ const onListening = () => {
22504
+ cleanup();
22521
22505
  if (portFileDir) {
22522
22506
  try {
22523
22507
  const portFile = path.join(portFileDir, DEV_PORT_FILENAME);
22524
- fs.writeFileSync(portFile, String(port), "utf-8");
22508
+ fs.writeFileSync(portFile, `${port} ${startPort}`, "utf-8");
22525
22509
  } catch {}
22526
22510
  writeStateFile(portFileDir, port, options?.serviceKey);
22527
22511
  }
22528
22512
  resolve(port);
22529
- });
22513
+ };
22514
+ const onError = (err) => {
22515
+ cleanup();
22516
+ if (err.code === "EADDRINUSE") tryNext(index + 1);
22517
+ else reject(err);
22518
+ };
22519
+ function cleanup() {
22520
+ server.removeListener("listening", onListening);
22521
+ server.removeListener("error", onError);
22522
+ }
22523
+ server.once("error", onError);
22524
+ server.once("listening", onListening);
22525
+ server.listen(port, host);
22530
22526
  }
22531
22527
  tryNext(0);
22532
22528
  });