@rebasepro/common 0.14.1 → 0.14.2-canary.g27a129e

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.
@@ -18,6 +18,13 @@ import type { OrderBySpec, OrderByTuple } from "@rebasepro/types";
18
18
  * the same value; the two are told apart by whether the first element is
19
19
  * itself an array, which no field name ever is.
20
20
  *
21
+ * This is also where a {@link RelationAggregateSort} object stops being an
22
+ * object. Above this function a sort key may be either spelling; below it,
23
+ * every key is a string — which is what `OrderByTuple`, the REST parameter, the
24
+ * driver contract and the cursor all already were. Doing it here means the one
25
+ * place that already collapses the two *shapes* of a sort also collapses the
26
+ * two *spellings* of a key, rather than every consumer learning about both.
27
+ *
21
28
  * @returns The keys in order of significance, or `undefined` for no sort. An
22
29
  * empty list also returns `undefined` — "sort by nothing" is no sort, and
23
30
  * letting `[]` through would have every layer below re-deciding what it meant.
package/dist/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, CANONICAL_TO_REST, DEFAULT_DATA_SOURCE_KEY, DEFAULT_LIST_LIMIT, EntityReference, EntityRelation, NULL_OPS, REST_TO_CANONICAL, RLS_ROLES_SQL, RLS_UID_SQL, getDataSourceCapabilities, getDeclaredSubcollections, isAnonymousUid, isManyToMany, isPostgresCollectionConfig, isRelationalCollectionConfig, policy, rewriteLegacyRlsFunctions, toCanonicalOp } from "@rebasepro/types";
1
+ import { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, ANONYMOUS_USER_IDS, CANONICAL_TO_REST, DEFAULT_DATA_SOURCE_KEY, DEFAULT_LIST_LIMIT, EntityReference, EntityRelation, NULL_OPS, REST_TO_CANONICAL, RLS_ROLES_SQL, RLS_UID_SQL, getDataSourceCapabilities, getDeclaredSubcollections, isAnonymousUid, isManyToMany, isPostgresCollectionConfig, isRelationAggregateSort, isRelationalCollectionConfig, policy, rewriteLegacyRlsFunctions, sortKeyToString, toCanonicalOp } from "@rebasepro/types";
2
2
  import { deepClone, firstFreeKey, generateForeignKeyName, getIn, getPolicyNamesForRules, getPolicyOperations, isDefaultFieldConfigId, mergeDeep, prettifyIdentifier, randomString, removeFunctions, toSnakeCase, toWireKey } from "@rebasepro/utils";
3
3
  import jsonLogic from "json-logic-js";
4
4
  import { deepEqual } from "fast-equals";
@@ -486,7 +486,7 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
486
486
  const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);
487
487
  const shared = {
488
488
  relationName,
489
- target,
489
+ target: () => unwrapModuleNamespace(target()),
490
490
  targetSlug: targetCollection.slug,
491
491
  onUpdate: relation.onUpdate,
492
492
  onDelete: relation.onDelete,
@@ -554,35 +554,67 @@ function describe(relation, sourceCollection, propertyKey) {
554
554
  return `Relation${name ? ` '${name}'` : ""} on '${sourceCollection.slug}'`;
555
555
  }
556
556
  /**
557
- * Call the `target` thunk, and translate the two ways an import cycle breaks it
558
- * into an error that names the cause.
557
+ * A module namespace, unwrapped to the collection it exports.
558
+ *
559
+ * A cycle transpiled to CommonJS does not hand the importing module the
560
+ * *default export* — it hands it the module object, `{ __esModule: true,
561
+ * default: … }`, captured before the exporting module finished evaluating. The
562
+ * `default` slot fills in later, so by the time a lazy `target` thunk runs the
563
+ * collection is sitting right there, one level down. Returning the namespace is
564
+ * never a thing a thunk means to do, and there is exactly one reading of it.
565
+ *
566
+ * Only unwrapped when the inner value is itself a collection: a `default` that
567
+ * is not one is a genuinely wrong thunk, and it should reach the error below
568
+ * rather than be quietly swapped in.
569
+ */
570
+ function unwrapModuleNamespace(value) {
571
+ if (!value || typeof value !== "object") return value;
572
+ if (value.slug) return value;
573
+ const inner = value.default;
574
+ return inner && typeof inner === "object" && inner.slug ? inner : value;
575
+ }
576
+ /**
577
+ * Call the `target` thunk, and translate the ways an import cycle breaks it into
578
+ * an error that names the cause — or, where the value is recoverable, into the
579
+ * collection the thunk meant.
559
580
  *
560
581
  * The thunk exists to defer the reference until every module has finished
561
- * evaluating, and for a cycle that closes at import time it does. What it cannot
562
- * defer is a cycle that leaves the binding permanently unusable, and there are
563
- * two shapes of that:
582
+ * evaluating, and for a cycle that closes at import time it does. Two cycles
583
+ * leave the binding permanently unusable:
564
584
  *
565
585
  * - **ESM/TDZ.** `const` and `class` bindings in a not-yet-evaluated module are
566
586
  * in the temporal dead zone, so reading one throws `ReferenceError: x is not
567
587
  * defined`. The stack points at the thunk — a one-line arrow function that is
568
588
  * obviously fine — and says nothing about the cycle that made it throw.
569
- * - **CJS interop.** The half-initialised module object has no `default` yet,
570
- * the import resolves to `undefined`, and the thunk returns it without
571
- * complaint. That one used to surface here as "did not resolve to a
589
+ * - **CJS interop, unresolved.** The half-initialised module object has no
590
+ * `default` yet, the import resolves to `undefined`, and the thunk returns it
591
+ * without complaint. That one used to surface here as "did not resolve to a
572
592
  * collection", which is true and unhelpful.
573
593
  *
574
594
  * Both mean the same thing, and the fix for both is the same: break the cycle,
575
595
  * or move the relation into the collection that does not close it.
596
+ *
597
+ * A third shape is *not* an error, and used to be reported as one. A loader that
598
+ * transpiles ESM to CJS — jiti, which is what `rebase generate-sdk` and
599
+ * `rebase build` load collections with — gives the module entered second in a
600
+ * cycle a namespace object rather than the default export, and never replaces it
601
+ * with a live binding. The thunk then returns `{ __esModule: true, default: … }`
602
+ * holding the fully-initialised collection. Native ESM resolves the same thunk
603
+ * to the collection directly, so this was a loader artefact reported as an
604
+ * authoring mistake, and the advice it gave — make the target a lazy thunk — was
605
+ * already satisfied by the code it was rejecting. Bidirectional relations make
606
+ * these cycles unavoidable, and the lazy thunk is this framework's own answer to
607
+ * them, so {@link unwrapModuleNamespace} takes the collection and moves on.
576
608
  */
577
609
  function callTarget(relation, sourceCollection, propertyKey, target) {
578
610
  let targetCollection;
579
611
  try {
580
- targetCollection = target();
612
+ targetCollection = unwrapModuleNamespace(target());
581
613
  } catch (error) {
582
614
  if (error instanceof ReferenceError) throw new Error(`${describe(relation, sourceCollection, propertyKey)} targets a collection that is not initialized yet — almost always an import cycle between the two collection files. Break the cycle (move the shared piece into a third module, or import the target lazily) so the target's module finishes evaluating before the registry is built.`, { cause: error });
583
615
  throw error;
584
616
  }
585
- if (!targetCollection?.slug) throw new Error(`${describe(relation, sourceCollection, propertyKey)} has a \`target\` that resolved to ${targetCollection === void 0 ? "`undefined`" : "something that is not a collection"}. ` + (targetCollection === void 0 ? "Under CommonJS interop an import cycle resolves the default import to `undefined`, so check whether this collection and its target import each other. Otherwise the thunk is returning the wrong value — it must return the collection itself, not a promise or a module." : "The thunk must return a collection config with a `slug`."));
617
+ if (!targetCollection?.slug) throw new Error(`${describe(relation, sourceCollection, propertyKey)} has a \`target\` that resolved to ${targetCollection === void 0 ? "`undefined`" : "something that is not a collection"}. ` + (targetCollection === void 0 ? "Under CommonJS interop an import cycle resolves the default import to `undefined`, so check whether this collection and its target import each other. Otherwise the thunk is returning the wrong value — it must return the collection itself, not a promise or a module." : typeof targetCollection.then === "function" ? "The thunk returned a promise — `target: () => import(\"./other\")` is asynchronous. Import the collection at the top of the file and return the binding: `target: () => otherCollection`." : "The thunk must return a collection config with a `slug`."));
586
618
  return targetCollection;
587
619
  }
588
620
  //#endregion
@@ -2786,6 +2818,7 @@ var REBASE_INTERNAL_TABLES = [
2786
2818
  "api_keys",
2787
2819
  "cron_logs",
2788
2820
  "cron_claims",
2821
+ "rate_limit_hits",
2789
2822
  "idempotency_keys",
2790
2823
  "entity_history",
2791
2824
  "branches",
@@ -3303,6 +3336,13 @@ var defaultUsersCollection = defineCollection({
3303
3336
  * the same value; the two are told apart by whether the first element is
3304
3337
  * itself an array, which no field name ever is.
3305
3338
  *
3339
+ * This is also where a {@link RelationAggregateSort} object stops being an
3340
+ * object. Above this function a sort key may be either spelling; below it,
3341
+ * every key is a string — which is what `OrderByTuple`, the REST parameter, the
3342
+ * driver contract and the cursor all already were. Doing it here means the one
3343
+ * place that already collapses the two *shapes* of a sort also collapses the
3344
+ * two *spellings* of a key, rather than every consumer learning about both.
3345
+ *
3306
3346
  * @returns The keys in order of significance, or `undefined` for no sort. An
3307
3347
  * empty list also returns `undefined` — "sort by nothing" is no sort, and
3308
3348
  * letting `[]` through would have every layer below re-deciding what it meant.
@@ -3310,7 +3350,8 @@ var defaultUsersCollection = defineCollection({
3310
3350
  function normalizeOrderBy(orderBy) {
3311
3351
  if (!orderBy || orderBy.length === 0) return void 0;
3312
3352
  const list = Array.isArray(orderBy[0]) ? orderBy : [orderBy];
3313
- return list.length > 0 ? list : void 0;
3353
+ if (list.length === 0) return void 0;
3354
+ return list.map(([key, direction]) => [sortKeyToString(key), direction]);
3314
3355
  }
3315
3356
  /**
3316
3357
  * The most significant sort key, for a caller that can only express one —
@@ -3365,14 +3406,16 @@ function parseOrderBySpecStrict(raw, order) {
3365
3406
  if (raw === void 0 || raw === null || raw === "") return void 0;
3366
3407
  if (typeof raw === "string") return normalizeDriverOrderBy(raw, order);
3367
3408
  if (!Array.isArray(raw) || raw.length === 0) throw new OrderBySpecError(`${typeof raw} is not a field name or a list of sort keys`);
3368
- if (typeof raw[0] === "string") return [toStrictTuple(raw, 0)];
3409
+ if (typeof raw[0] === "string" || isRelationAggregateSort(raw[0])) return [toStrictTuple(raw, 0)];
3369
3410
  return raw.map(toStrictTuple);
3370
3411
  }
3371
3412
  function toStrictTuple(raw, index) {
3372
- if (!Array.isArray(raw) || typeof raw[0] !== "string" || raw[0].trim() === "") throw new OrderBySpecError(`entry ${index} has no field name`);
3413
+ if (!Array.isArray(raw)) throw new OrderBySpecError(`entry ${index} has no field name`);
3414
+ const key = isRelationAggregateSort(raw[0]) ? sortKeyToString(raw[0]) : raw[0];
3415
+ if (typeof key !== "string" || key.trim() === "") throw new OrderBySpecError(`entry ${index} has no field name`);
3373
3416
  const direction = raw[1];
3374
3417
  if (direction !== void 0 && direction !== "asc" && direction !== "desc") throw new OrderBySpecError(`entry ${index} has direction '${String(direction)}'`);
3375
- return [raw[0], direction ?? "asc"];
3418
+ return [key, direction ?? "asc"];
3376
3419
  }
3377
3420
  /**
3378
3421
  * Serialize a sort to the wire.