@rpcbase/db 0.28.0 → 0.29.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ### `LocalizedString` / `zI18nString()`
4
4
 
5
- `zI18nString()` (alias de `zLocalizedString()`) décrit une string localisée sous forme d’objet, avec des clés en *language codes* (BCP47) :
5
+ `zI18nString()` (alias of `zLocalizedString()`) describes a localized string as an object, keyed by *language codes* (BCP47):
6
6
 
7
7
  ```ts
8
8
  import { z } from "zod"
@@ -15,11 +15,11 @@ const ZPost = z.object({
15
15
  const schema = zodSchema(ZPost)
16
16
  ```
17
17
 
18
- **Côté Mongoose (important)**
18
+ **Mongoose-side (important)**
19
19
 
20
- - Le champ est stocké comme un objet et typé `SchemaTypes.Mixed` (pas un `Map`).
21
- - À la lecture, le getter Mongoose renvoie un objet “proxy” avec fallback automatique : `fr-FR` retombe sur `fr` si `fr-FR` nexiste pas.
20
+ - The field is stored as an object and typed as `SchemaTypes.Mixed` (not a `Map`).
21
+ - On read, the Mongoose getter returns a “proxy” object with automatic fallback: `fr-FR` falls back to `fr` if `fr-FR` doesnt exist.
22
22
  - `doc.title["fr-FR"]` → fallback
23
- - `doc.title.get("fr-FR")` → fallback (helper de lecture)
24
- - `doc.title.getExact("fr-FR")` → exact (sans fallback)
25
- - À l’écriture, comme cest du `Mixed`, évite les mutations profondes silencieuses : préfère remplacer l’objet (`doc.title = { ...doc.title, fr: "Salut" }`) ou appelle `doc.markModified("title")`.
23
+ - `doc.title.get("fr-FR")` → fallback (read helper)
24
+ - `doc.title.getExact("fr-FR")` → exact (no fallback)
25
+ - On write, because its `Mixed`, avoid silent deep mutations: prefer replacing the object (`doc.title = { ...doc.title, fr: "Salut" }`) or call `doc.markModified("title")`.
@@ -0,0 +1,36 @@
1
+ import { default as mongoose } from '../../../vite/node_modules/mongoose';
2
+ import { AclAction, AppAbility } from './types';
3
+ type StoredAcl = {
4
+ ability: AppAbility;
5
+ action?: AclAction;
6
+ };
7
+ type PipelineStageLike = Record<string, unknown>;
8
+ declare const mongooseAclPluginBrand: unique symbol;
9
+ declare module '../../../vite/node_modules/mongoose' {
10
+ interface QueryOptions<DocType = unknown> {
11
+ rbAcl?: StoredAcl;
12
+ [mongooseAclPluginBrand]?: DocType;
13
+ }
14
+ interface AggregateOptions {
15
+ rbAcl?: StoredAcl;
16
+ }
17
+ interface Query<ResultType, DocType, THelpers = {}, RawDocType = unknown, QueryOp = "find", TDocOverrides = Record<string, never>> {
18
+ [mongooseAclPluginBrand]?: [ResultType, DocType, THelpers, RawDocType, QueryOp, TDocOverrides];
19
+ acl: (ability: AppAbility, action?: AclAction) => this;
20
+ }
21
+ interface Aggregate<ResultType> {
22
+ [mongooseAclPluginBrand]?: ResultType;
23
+ acl: (ability: AppAbility, action?: AclAction) => this;
24
+ }
25
+ interface Model<TRawDocType, TQueryHelpers, TInstanceMethods, TVirtuals, THydratedDocumentType, TSchema, TLeanResultType> {
26
+ [mongooseAclPluginBrand]?: [TRawDocType, TQueryHelpers, TInstanceMethods, TVirtuals, THydratedDocumentType, TSchema, TLeanResultType];
27
+ acl: (ability: AppAbility) => this;
28
+ }
29
+ }
30
+ export declare const mongooseAclPlugin: (schema: mongoose.Schema) => void;
31
+ export declare const _private: {
32
+ injectAggregateMatch: (pipeline: PipelineStageLike[], match: Record<string, unknown>) => void;
33
+ mergeMongoQuery: (left: unknown, right: Record<string, unknown>) => Record<string, unknown>;
34
+ };
35
+ export {};
36
+ //# sourceMappingURL=mongooseAclPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongooseAclPlugin.d.ts","sourceRoot":"","sources":["../../src/acl/mongooseAclPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAG/B,OAAO,KAAK,EAAE,SAAS,EAAkB,UAAU,EAAE,MAAM,SAAS,CAAA;AAGpE,KAAK,SAAS,GAAG;IACf,OAAO,EAAE,UAAU,CAAA;IACnB,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,CAAA;AAID,KAAK,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AA6HhD,OAAO,CAAC,MAAM,sBAAsB,EAAE,OAAO,MAAM,CAAA;AAEnD,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,YAAY,CAAC,OAAO,GAAG,OAAO;QACtC,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,CAAC,sBAAsB,CAAC,CAAC,EAAE,OAAO,CAAC;KACpC;IAED,UAAU,gBAAgB;QACxB,KAAK,CAAC,EAAE,SAAS,CAAC;KACnB;IAED,UAAU,KAAK,CACb,UAAU,EACV,OAAO,EACP,QAAQ,GAAG,EAAE,EACb,UAAU,GAAG,OAAO,EACpB,OAAO,GAAG,MAAM,EAChB,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAErC,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QAC/F,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;KACxD;IAED,UAAU,SAAS,CAAC,UAAU;QAC5B,CAAC,sBAAsB,CAAC,CAAC,EAAE,UAAU,CAAC;QACtC,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;KACxD;IAED,UAAU,KAAK,CACb,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,qBAAqB,EACrB,OAAO,EACP,eAAe;QAEf,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,SAAS,EAAE,qBAAqB,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;QACtI,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;KACpC;CACF;AAED,eAAO,MAAM,iBAAiB,GAAI,QAAQ,QAAQ,CAAC,MAAM,KAAG,IAsE3D,CAAA;AAED,eAAO,MAAM,QAAQ;qCAjMmB,iBAAiB,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;4BA3CnE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CA+O/F,CAAA"}
package/dist/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export * from './createModels';
4
4
  export * from './loadModel';
5
5
  export * from './tenantFilesystemDb';
6
6
  export * from './acl';
7
+ export * from './pagination';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,OAAO,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,sBAAsB,CAAA;AACpC,cAAc,OAAO,CAAA;AACrB,cAAc,cAAc,CAAA"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { z as z2 } from "zod";
5
5
  import assert from "assert";
6
6
  import { accessibleBy, accessibleRecordsPlugin } from "@casl/mongoose";
7
7
  import { AbilityBuilder, createMongoAbility, subject } from "@casl/ability";
8
+ import { timingSafeEqual, createHmac } from "node:crypto";
8
9
  const ZRBUser = z.object({
9
10
  email: z.string().email().optional(),
10
11
  password: z.string(),
@@ -1424,8 +1425,439 @@ const can = (ability, action, subjectType, object) => {
1424
1425
  }
1425
1426
  return ability.can(action, subjectType);
1426
1427
  };
1428
+ const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
1429
+ const mergeMongoQuery = (left, right) => {
1430
+ const leftQuery = isRecord(left) ? left : {};
1431
+ if (Object.keys(leftQuery).length === 0) return right;
1432
+ if (Object.keys(right).length === 0) return leftQuery;
1433
+ return { $and: [leftQuery, right] };
1434
+ };
1435
+ const getQueryOptions$1 = (query) => {
1436
+ if (!query || typeof query !== "object") return void 0;
1437
+ if (!("getOptions" in query) || typeof query.getOptions !== "function") return void 0;
1438
+ return query.getOptions();
1439
+ };
1440
+ const getStoredAclFromQuery = (query) => {
1441
+ const options = getQueryOptions$1(query);
1442
+ const raw = options?.rbAcl;
1443
+ if (!isRecord(raw)) return null;
1444
+ if (!("ability" in raw)) return null;
1445
+ return raw;
1446
+ };
1447
+ const getStoredAclFromAggregate = (aggregate) => {
1448
+ if (!aggregate || typeof aggregate !== "object") return null;
1449
+ const raw = aggregate.options;
1450
+ if (!isRecord(raw)) return null;
1451
+ const acl = raw.rbAcl;
1452
+ if (!isRecord(acl)) return null;
1453
+ if (!("ability" in acl)) return null;
1454
+ return acl;
1455
+ };
1456
+ const addQueryAclFilter = (query, action) => {
1457
+ const storedAcl = getStoredAclFromQuery(query);
1458
+ if (!storedAcl) return;
1459
+ const ability = storedAcl.ability;
1460
+ const resolvedAction = storedAcl.action ?? action;
1461
+ const modelName = query.model.modelName;
1462
+ const accessQuery = accessibleBy(ability, resolvedAction).ofType(modelName);
1463
+ query.and([accessQuery]);
1464
+ };
1465
+ const injectAggregateMatch = (pipeline, match) => {
1466
+ if (pipeline.length === 0) {
1467
+ pipeline.unshift({ $match: match });
1468
+ return;
1469
+ }
1470
+ const first = pipeline[0];
1471
+ if (!isRecord(first)) {
1472
+ pipeline.unshift({ $match: match });
1473
+ return;
1474
+ }
1475
+ if ("$geoNear" in first) {
1476
+ const geoNear = first.$geoNear;
1477
+ if (isRecord(geoNear)) {
1478
+ geoNear.query = mergeMongoQuery(geoNear.query, match);
1479
+ return;
1480
+ }
1481
+ }
1482
+ if ("$search" in first || "$vectorSearch" in first || "$searchMeta" in first) {
1483
+ pipeline.splice(1, 0, { $match: match });
1484
+ return;
1485
+ }
1486
+ pipeline.unshift({ $match: match });
1487
+ };
1488
+ const addAggregateAclFilter = (aggregate, action) => {
1489
+ const storedAcl = getStoredAclFromAggregate(aggregate);
1490
+ if (!storedAcl) return;
1491
+ const ability = storedAcl.ability;
1492
+ const resolvedAction = storedAcl.action ?? action;
1493
+ const modelName = aggregate.model().modelName;
1494
+ const accessQuery = accessibleBy(ability, resolvedAction).ofType(modelName);
1495
+ injectAggregateMatch(aggregate.pipeline(), accessQuery);
1496
+ };
1497
+ const patchAggregateAcl = () => {
1498
+ const globalKey = /* @__PURE__ */ Symbol.for("@rpcbase/db/acl/mongooseAggregateAclPatched");
1499
+ const globalState = globalThis;
1500
+ if (globalState[globalKey]) return;
1501
+ globalState[globalKey] = true;
1502
+ const AggregatePrototype = mongoose.Aggregate.prototype;
1503
+ if (typeof AggregatePrototype.acl === "function") return;
1504
+ AggregatePrototype.acl = function(ability, action = "read") {
1505
+ this.option({ rbAcl: { ability, action } });
1506
+ return this;
1507
+ };
1508
+ };
1509
+ const createModelAclProxy = (model, ability) => {
1510
+ return new Proxy(model, {
1511
+ get(target, prop, receiver) {
1512
+ const value = Reflect.get(target, prop, receiver);
1513
+ if (typeof value !== "function") return value;
1514
+ return (...args) => {
1515
+ const result = Reflect.apply(value, target, args);
1516
+ if (result && typeof result === "object" && "acl" in result && typeof result.acl === "function") {
1517
+ return result.acl(ability);
1518
+ }
1519
+ return result;
1520
+ };
1521
+ }
1522
+ });
1523
+ };
1524
+ const mongooseAclPlugin = (schema) => {
1525
+ patchAggregateAcl();
1526
+ schema.query.acl = function(ability, action) {
1527
+ this.setOptions({ rbAcl: { ability, action } });
1528
+ return this;
1529
+ };
1530
+ schema.statics.acl = function(ability) {
1531
+ return createModelAclProxy(this, ability);
1532
+ };
1533
+ schema.pre("aggregate", function() {
1534
+ addAggregateAclFilter(this, "read");
1535
+ });
1536
+ schema.pre("countDocuments", function() {
1537
+ addQueryAclFilter(this, "read");
1538
+ });
1539
+ schema.pre("deleteMany", function() {
1540
+ addQueryAclFilter(this, "delete");
1541
+ });
1542
+ schema.pre("deleteOne", function() {
1543
+ addQueryAclFilter(this, "delete");
1544
+ });
1545
+ schema.pre("distinct", function() {
1546
+ addQueryAclFilter(this, "read");
1547
+ });
1548
+ schema.pre("find", function() {
1549
+ addQueryAclFilter(this, "read");
1550
+ });
1551
+ schema.pre("findOne", function() {
1552
+ addQueryAclFilter(this, "read");
1553
+ });
1554
+ schema.pre("findOneAndDelete", function() {
1555
+ addQueryAclFilter(this, "delete");
1556
+ });
1557
+ schema.pre("findOneAndReplace", function() {
1558
+ addQueryAclFilter(this, "update");
1559
+ });
1560
+ schema.pre("findOneAndUpdate", function() {
1561
+ addQueryAclFilter(this, "update");
1562
+ });
1563
+ schema.pre("replaceOne", function() {
1564
+ addQueryAclFilter(this, "update");
1565
+ });
1566
+ schema.pre("updateMany", function() {
1567
+ addQueryAclFilter(this, "update");
1568
+ });
1569
+ schema.pre("updateOne", function() {
1570
+ addQueryAclFilter(this, "update");
1571
+ });
1572
+ };
1573
+ class PaginationValidationError extends Error {
1574
+ code = "invalid_pagination";
1575
+ statusCode = 400;
1576
+ constructor(message, options) {
1577
+ super(message, options);
1578
+ this.name = "PaginationValidationError";
1579
+ }
1580
+ }
1581
+ const isPaginationValidationError = (error) => {
1582
+ if (!error || typeof error !== "object") return false;
1583
+ const anyError = error;
1584
+ return anyError.name === "PaginationValidationError" && anyError.code === "invalid_pagination" && anyError.statusCode === 400;
1585
+ };
1586
+ const DISALLOWED_MONGO_FIELD_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1587
+ const PAGINATION_LIMIT_MAX = 128;
1588
+ const normalizePaginationSpec = (spec) => {
1589
+ if (!spec || typeof spec !== "object") throw new PaginationValidationError("Invalid PaginationSpec");
1590
+ const limit = normalizeLimit(spec.limit);
1591
+ const direction = spec.direction ?? "next";
1592
+ if (direction !== "next" && direction !== "prev") throw new PaginationValidationError("Invalid pagination direction");
1593
+ if (!Array.isArray(spec.sort) || spec.sort.length === 0) throw new PaginationValidationError("Invalid pagination sort");
1594
+ const sort = spec.sort.map(({ field, order }) => ({
1595
+ field: assertSafeMongoFieldPath(field),
1596
+ order: normalizeOrder(order)
1597
+ }));
1598
+ const seenFields = /* @__PURE__ */ new Set();
1599
+ for (const { field } of sort) {
1600
+ if (seenFields.has(field)) throw new PaginationValidationError(`Duplicate pagination sort field: ${field}`);
1601
+ seenFields.add(field);
1602
+ }
1603
+ const primaryOrder = sort[0]?.order;
1604
+ if (!seenFields.has("_id")) {
1605
+ sort.push({ field: "_id", order: primaryOrder });
1606
+ }
1607
+ return {
1608
+ ...spec,
1609
+ limit,
1610
+ direction,
1611
+ sort
1612
+ };
1613
+ };
1614
+ const normalizeLimit = (limit) => {
1615
+ if (typeof limit !== "number" || !Number.isFinite(limit) || !Number.isInteger(limit) || limit <= 0) {
1616
+ throw new PaginationValidationError("Invalid pagination limit");
1617
+ }
1618
+ if (limit > PAGINATION_LIMIT_MAX) {
1619
+ throw new PaginationValidationError("Invalid pagination limit");
1620
+ }
1621
+ return limit;
1622
+ };
1623
+ const normalizeOrder = (order) => {
1624
+ if (order !== "asc" && order !== "desc") throw new PaginationValidationError("Invalid pagination order");
1625
+ return order;
1626
+ };
1627
+ const assertSafeMongoFieldPath = (field) => {
1628
+ if (typeof field !== "string" || field.length === 0) throw new PaginationValidationError("Invalid pagination sort field");
1629
+ if (field.startsWith("$")) throw new PaginationValidationError("Invalid pagination sort field");
1630
+ const parts = field.split(".");
1631
+ for (const part of parts) {
1632
+ if (part.length === 0) throw new PaginationValidationError("Invalid pagination sort field");
1633
+ if (DISALLOWED_MONGO_FIELD_SEGMENTS.has(part)) throw new PaginationValidationError("Invalid pagination sort field");
1634
+ if (!/^[a-zA-Z0-9_]+$/.test(part)) throw new PaginationValidationError("Invalid pagination sort field");
1635
+ }
1636
+ return field;
1637
+ };
1638
+ const encodePaginationCursor = (spec, node, options) => {
1639
+ const normalized = normalizePaginationSpec(spec);
1640
+ const values = /* @__PURE__ */ Object.create(null);
1641
+ for (const { field } of normalized.sort) {
1642
+ const value = readFieldValue(node, field);
1643
+ if (typeof value === "undefined") {
1644
+ throw new Error(`Pagination cursor encode failed (missing field: ${field})`);
1645
+ }
1646
+ values[field] = encodeCursorValue(field, value);
1647
+ }
1648
+ const payload = { v: 1, values };
1649
+ const payloadB64 = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
1650
+ const sigB64 = signCursorPayloadB64(payloadB64, options.signingSecret);
1651
+ return `${payloadB64}.${sigB64}`;
1652
+ };
1653
+ const decodePaginationCursor = (spec, cursor, options) => {
1654
+ const normalized = normalizePaginationSpec(spec);
1655
+ const [payloadB64, sigB64, ...rest] = cursor.split(".");
1656
+ if (rest.length > 0) throw new PaginationValidationError("Invalid pagination cursor format");
1657
+ if (!sigB64) throw new PaginationValidationError("Invalid pagination cursor format");
1658
+ verifyCursorSignature(payloadB64, sigB64, options.signingSecret);
1659
+ const payloadRaw = Buffer.from(payloadB64, "base64url").toString("utf8");
1660
+ let payloadUnknown;
1661
+ try {
1662
+ payloadUnknown = JSON.parse(payloadRaw);
1663
+ } catch {
1664
+ throw new PaginationValidationError("Invalid pagination cursor payload");
1665
+ }
1666
+ if (!payloadUnknown || typeof payloadUnknown !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
1667
+ const payload = payloadUnknown;
1668
+ if (payload.v !== 1) throw new PaginationValidationError("Unsupported pagination cursor version");
1669
+ if (!payload.values || typeof payload.values !== "object") throw new PaginationValidationError("Invalid pagination cursor payload");
1670
+ const decoded = /* @__PURE__ */ Object.create(null);
1671
+ for (const { field } of normalized.sort) {
1672
+ if (!Object.prototype.hasOwnProperty.call(payload.values, field)) {
1673
+ throw new PaginationValidationError(`Pagination cursor missing field: ${field}`);
1674
+ }
1675
+ const encodedValue = payload.values[field];
1676
+ decoded[field] = decodeCursorValue(field, encodedValue);
1677
+ }
1678
+ return decoded;
1679
+ };
1680
+ const signCursorPayloadB64 = (payloadB64, secret) => {
1681
+ return createHmac("sha256", secret).update(payloadB64, "utf8").digest("base64url");
1682
+ };
1683
+ const verifyCursorSignature = (payloadB64, sigB64, secret) => {
1684
+ const expectedSigB64 = signCursorPayloadB64(payloadB64, secret);
1685
+ const a = Buffer.from(sigB64, "utf8");
1686
+ const b = Buffer.from(expectedSigB64, "utf8");
1687
+ if (a.length !== b.length || !timingSafeEqual(a, b)) {
1688
+ throw new PaginationValidationError("Invalid pagination cursor signature");
1689
+ }
1690
+ };
1691
+ const encodeCursorValue = (field, value) => {
1692
+ if (value === null) return null;
1693
+ if (field === "_id") {
1694
+ if (value instanceof Types.ObjectId) return { $oid: value.toHexString() };
1695
+ throw new Error("Pagination cursor encode failed (_id must be an ObjectId)");
1696
+ }
1697
+ if (value instanceof Date) return { $date: value.toISOString() };
1698
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
1699
+ if (value instanceof Types.ObjectId) return { $oid: value.toHexString() };
1700
+ throw new Error(`Unsupported pagination cursor value type for field: ${field}`);
1701
+ };
1702
+ const decodeCursorValue = (field, value) => {
1703
+ if (value === null) return null;
1704
+ if (typeof value === "string") {
1705
+ if (field === "_id") throw new PaginationValidationError(`Invalid pagination cursor ObjectId for field: ${field}`);
1706
+ return value;
1707
+ }
1708
+ if (typeof value === "number" || typeof value === "boolean") return value;
1709
+ if (!value || typeof value !== "object") throw new PaginationValidationError(`Invalid pagination cursor value for field: ${field}`);
1710
+ if ("$date" in value) {
1711
+ if (typeof value.$date !== "string") throw new PaginationValidationError(`Invalid pagination cursor date for field: ${field}`);
1712
+ const d = new Date(value.$date);
1713
+ if (Number.isNaN(d.getTime())) throw new PaginationValidationError(`Invalid pagination cursor date for field: ${field}`);
1714
+ return d;
1715
+ }
1716
+ if ("$oid" in value) {
1717
+ if (typeof value.$oid !== "string" || !Types.ObjectId.isValid(value.$oid)) {
1718
+ throw new PaginationValidationError(`Invalid pagination cursor ObjectId for field: ${field}`);
1719
+ }
1720
+ return new Types.ObjectId(value.$oid);
1721
+ }
1722
+ throw new PaginationValidationError(`Invalid pagination cursor value for field: ${field}`);
1723
+ };
1724
+ const readFieldValue = (node, field) => {
1725
+ if (!node || typeof node !== "object") return void 0;
1726
+ if ("get" in node && typeof node.get === "function") {
1727
+ return node.get(field);
1728
+ }
1729
+ return field.split(".").reduce((acc, key) => {
1730
+ if (!acc || typeof acc !== "object") return void 0;
1731
+ return acc[key];
1732
+ }, node);
1733
+ };
1734
+ const compileMongoPagination = (spec, options) => {
1735
+ const normalized = normalizePaginationSpec(spec);
1736
+ const mongoLimit = normalized.limit + 1;
1737
+ const mongoSort = toMongoSort(normalized);
1738
+ if (normalized.cursor == null) {
1739
+ return {
1740
+ spec: normalized,
1741
+ mongoFilterDelta: {},
1742
+ mongoSort,
1743
+ mongoLimit
1744
+ };
1745
+ }
1746
+ if (typeof normalized.cursor !== "string" || normalized.cursor.length === 0) {
1747
+ throw new PaginationValidationError("Invalid pagination cursor");
1748
+ }
1749
+ const cursorValues = decodePaginationCursor(normalized, normalized.cursor, options.cursor);
1750
+ const mongoFilterDelta = buildKeysetFilterDelta(normalized, cursorValues);
1751
+ return {
1752
+ spec: normalized,
1753
+ mongoFilterDelta,
1754
+ mongoSort,
1755
+ mongoLimit
1756
+ };
1757
+ };
1758
+ const toMongoSort = (spec) => {
1759
+ const mongoSort = /* @__PURE__ */ Object.create(null);
1760
+ for (const { field, order } of spec.sort) {
1761
+ const forQueryOrder = spec.direction === "prev" ? invertOrder(order) : order;
1762
+ mongoSort[field] = forQueryOrder === "asc" ? 1 : -1;
1763
+ }
1764
+ return mongoSort;
1765
+ };
1766
+ const buildKeysetFilterDelta = (spec, cursorValues) => {
1767
+ const branches = [];
1768
+ for (let i = 0; i < spec.sort.length; i++) {
1769
+ const current = spec.sort[i];
1770
+ if (!current) continue;
1771
+ const and = [];
1772
+ for (let j = 0; j < i; j++) {
1773
+ const prev = spec.sort[j];
1774
+ if (!prev) continue;
1775
+ const eq = /* @__PURE__ */ Object.create(null);
1776
+ eq[prev.field] = cursorValues[prev.field];
1777
+ and.push(eq);
1778
+ }
1779
+ const op = getKeysetOp(current.order, spec.direction);
1780
+ const cmpOp = /* @__PURE__ */ Object.create(null);
1781
+ cmpOp[op] = cursorValues[current.field];
1782
+ const cmp = /* @__PURE__ */ Object.create(null);
1783
+ cmp[current.field] = cmpOp;
1784
+ and.push(cmp);
1785
+ branches.push(and.length === 1 ? and[0] : { $and: and });
1786
+ }
1787
+ return { $or: branches };
1788
+ };
1789
+ const getKeysetOp = (order, direction) => {
1790
+ if (direction === "next") return order === "asc" ? "$gt" : "$lt";
1791
+ return order === "asc" ? "$lt" : "$gt";
1792
+ };
1793
+ const invertOrder = (order) => {
1794
+ return order === "asc" ? "desc" : "asc";
1795
+ };
1796
+ const materializeMongoPagination = (compiled, fetchedNodes, options) => {
1797
+ const limit = compiled.spec.limit;
1798
+ const hasMore = fetchedNodes.length > limit;
1799
+ const trimmed = fetchedNodes.slice(0, limit);
1800
+ const nodes = compiled.spec.direction === "prev" ? trimmed.reverse() : trimmed;
1801
+ const hasPrevPage = compiled.spec.direction === "next" ? Boolean(compiled.spec.cursor) : hasMore;
1802
+ const hasNextPage = compiled.spec.direction === "next" ? hasMore : Boolean(compiled.spec.cursor);
1803
+ const pageInfo = {
1804
+ hasNextPage,
1805
+ hasPrevPage
1806
+ };
1807
+ if (nodes.length === 0) {
1808
+ return { nodes, pageInfo };
1809
+ }
1810
+ if (hasPrevPage) {
1811
+ pageInfo.prevCursor = encodePaginationCursor(compiled.spec, nodes[0], options.cursor);
1812
+ }
1813
+ if (hasNextPage) {
1814
+ pageInfo.nextCursor = encodePaginationCursor(compiled.spec, nodes[nodes.length - 1], options.cursor);
1815
+ }
1816
+ return { nodes, pageInfo };
1817
+ };
1818
+ const MongoAdapter = {
1819
+ applyPagination: (query, compiled) => {
1820
+ query.where(compiled.mongoFilterDelta);
1821
+ query.sort(compiled.mongoSort);
1822
+ query.limit(compiled.mongoLimit);
1823
+ return query;
1824
+ }
1825
+ };
1826
+ const paginateMongoQuery = async (query, pagination, options) => {
1827
+ const compiled = compileMongoPagination(pagination, { cursor: options.cursor });
1828
+ MongoAdapter.applyPagination(query, compiled);
1829
+ const fetchedNodes = await query.exec();
1830
+ if (!Array.isArray(fetchedNodes)) {
1831
+ throw new Error("paginateMongoQuery expects query.exec() to return an array");
1832
+ }
1833
+ return materializeMongoPagination(compiled, fetchedNodes, { cursor: options.cursor });
1834
+ };
1835
+ const getQueryOptions = (query) => {
1836
+ if (!query || typeof query !== "object") return void 0;
1837
+ if (!("getOptions" in query) || typeof query.getOptions !== "function") return void 0;
1838
+ return query.getOptions();
1839
+ };
1840
+ const getPaginationFromOptions = (query) => {
1841
+ const options = getQueryOptions(query);
1842
+ return options?.pagination;
1843
+ };
1844
+ const getCursorFromOptions = (query) => {
1845
+ const options = getQueryOptions(query);
1846
+ return options?.paginationCursor;
1847
+ };
1848
+ const mongoPaginationPlugin = (schema, pluginOptions) => {
1849
+ schema.query.paginate = async function(pagination, options) {
1850
+ const spec = pagination ?? getPaginationFromOptions(this);
1851
+ if (!spec) throw new Error("Missing pagination spec");
1852
+ const cursor = options?.cursor ?? getCursorFromOptions(this) ?? pluginOptions?.cursor;
1853
+ if (!cursor?.signingSecret) throw new Error("Missing pagination cursor signingSecret");
1854
+ return await paginateMongoQuery(this, spec, { cursor });
1855
+ };
1856
+ };
1427
1857
  const rtsChangeLogApplied = /* @__PURE__ */ new WeakSet();
1428
1858
  const accessibleRecordsApplied = /* @__PURE__ */ new WeakSet();
1859
+ const aclPluginApplied = /* @__PURE__ */ new WeakSet();
1860
+ const paginationPluginApplied = /* @__PURE__ */ new WeakSet();
1429
1861
  let cachedModels = null;
1430
1862
  const isRtsChangelogExcludedModelName = (name) => name.startsWith("RB");
1431
1863
  const assertSchema = (exportName, value) => {
@@ -1445,6 +1877,14 @@ const buildAppModels = (modules) => Object.entries(modules).filter(([key]) => ke
1445
1877
  schema.plugin(accessibleRecordsPlugin);
1446
1878
  accessibleRecordsApplied.add(schema);
1447
1879
  }
1880
+ if (!aclPluginApplied.has(schema)) {
1881
+ schema.plugin(mongooseAclPlugin);
1882
+ aclPluginApplied.add(schema);
1883
+ }
1884
+ if (!paginationPluginApplied.has(schema)) {
1885
+ schema.plugin(mongoPaginationPlugin);
1886
+ paginationPluginApplied.add(schema);
1887
+ }
1448
1888
  if (!isRtsChangelogExcludedModelName(name)) {
1449
1889
  if (!rtsChangeLogApplied.has(schema)) {
1450
1890
  schema.plugin(rtsChangeLogPlugin);
@@ -1463,6 +1903,14 @@ const buildFrameworkModels = () => Object.entries(frameworkSchemas).filter(([key
1463
1903
  schema.plugin(accessibleRecordsPlugin);
1464
1904
  accessibleRecordsApplied.add(schema);
1465
1905
  }
1906
+ if (!aclPluginApplied.has(schema)) {
1907
+ schema.plugin(mongooseAclPlugin);
1908
+ aclPluginApplied.add(schema);
1909
+ }
1910
+ if (!paginationPluginApplied.has(schema)) {
1911
+ schema.plugin(mongoPaginationPlugin);
1912
+ paginationPluginApplied.add(schema);
1913
+ }
1466
1914
  if (!isRtsChangelogExcludedModelName(name)) {
1467
1915
  if (!rtsChangeLogApplied.has(schema)) {
1468
1916
  schema.plugin(rtsChangeLogPlugin);
@@ -1562,6 +2010,7 @@ const getTenantFilesystemDbFromCtx = async (ctx) => {
1562
2010
  };
1563
2011
  export {
1564
2012
  LANGUAGE_CODE_REGEX,
2013
+ PaginationValidationError,
1565
2014
  RBNotificationPolicy,
1566
2015
  RBNotificationSchema,
1567
2016
  RBNotificationSettingsPolicy,
@@ -1607,8 +2056,10 @@ export {
1607
2056
  getTenantFilesystemDbFromCtx,
1608
2057
  getTenantFilesystemDbName,
1609
2058
  getTenantRolesFromSessionUser,
2059
+ isPaginationValidationError,
1610
2060
  loadModel,
1611
2061
  loadRbModel,
2062
+ mongoPaginationPlugin,
1612
2063
  registerPoliciesFromModules,
1613
2064
  registerPolicy,
1614
2065
  resolveLocalizedString,
@@ -0,0 +1,14 @@
1
+ import { PaginationSpec } from '../../../api/src';
2
+ import { PaginationCursorCodecOptions } from './cursor';
3
+ import { NormalizedPaginationSpec } from './normalizeSpec';
4
+ export type CompiledMongoPagination = {
5
+ spec: NormalizedPaginationSpec;
6
+ mongoFilterDelta: Record<string, unknown>;
7
+ mongoSort: Record<string, 1 | -1>;
8
+ mongoLimit: number;
9
+ };
10
+ export type CompileMongoPaginationOptions = {
11
+ cursor: PaginationCursorCodecOptions;
12
+ };
13
+ export declare const compileMongoPagination: (spec: PaginationSpec, options: CompileMongoPaginationOptions) => CompiledMongoPagination;
14
+ //# sourceMappingURL=compileMongoPagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compileMongoPagination.d.ts","sourceRoot":"","sources":["../../src/pagination/compileMongoPagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,cAAc,CAAA;AAEnE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAG5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAI/D,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG;IAC1C,MAAM,EAAE,4BAA4B,CAAC;CACtC,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,MAAM,cAAc,EACpB,SAAS,6BAA6B,KACrC,uBA4BF,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { PaginationSpec } from '../../../api/src';
2
+ export type PaginationCursorCodecOptions = {
3
+ signingSecret: string | Buffer;
4
+ };
5
+ export declare const encodePaginationCursor: (spec: PaginationSpec, node: unknown, options: PaginationCursorCodecOptions) => string;
6
+ export declare const decodePaginationCursor: (spec: PaginationSpec, cursor: string, options: PaginationCursorCodecOptions) => Record<string, unknown>;
7
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/pagination/cursor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAmBlD,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,MAAM,cAAc,EACpB,MAAM,OAAO,EACb,SAAS,4BAA4B,KACpC,MAiBF,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,MAAM,cAAc,EACpB,QAAQ,MAAM,EACd,SAAS,4BAA4B,KACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAiCxB,CAAA"}
@@ -0,0 +1,8 @@
1
+ export type PaginationErrorCode = "invalid_pagination";
2
+ export declare class PaginationValidationError extends Error {
3
+ readonly code: PaginationErrorCode;
4
+ readonly statusCode = 400;
5
+ constructor(message: string, options?: ErrorOptions);
6
+ }
7
+ export declare const isPaginationValidationError: (error: unknown) => error is PaginationValidationError;
8
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/pagination/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAA;AAEtD,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAuB;IACzD,QAAQ,CAAC,UAAU,OAAM;gBAEb,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAIpD;AAED,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,yBAQrE,CAAA"}
@@ -0,0 +1,5 @@
1
+ export type { PaginationCursorCodecOptions } from './cursor';
2
+ export type { MongoPaginationPluginOptions } from './mongoPaginationPlugin';
3
+ export { mongoPaginationPlugin } from './mongoPaginationPlugin';
4
+ export { PaginationValidationError, isPaginationValidationError } from './errors';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pagination/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAC5D,YAAY,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { PaginationSpec } from '../../../api/src';
2
+ export type MongoIndexHint = Record<string, 1 | -1>;
3
+ export declare const getMongoPaginationIndexHint: (spec: PaginationSpec, baseFilter: Record<string, unknown>) => MongoIndexHint;
4
+ //# sourceMappingURL=indexHint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexHint.d.ts","sourceRoot":"","sources":["../../src/pagination/indexHint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAKlD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAEnD,eAAO,MAAM,2BAA2B,GACtC,MAAM,cAAc,EACpB,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,cAeF,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { PaginationResponse } from '../../../api/src';
2
+ import { PaginationCursorCodecOptions } from './cursor';
3
+ import { CompiledMongoPagination } from './compileMongoPagination';
4
+ export type MaterializePaginationOptions = {
5
+ cursor: PaginationCursorCodecOptions;
6
+ };
7
+ export declare const materializeMongoPagination: <TNode>(compiled: CompiledMongoPagination, fetchedNodes: TNode[], options: MaterializePaginationOptions) => PaginationResponse<TNode>;
8
+ //# sourceMappingURL=materializePagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materializePagination.d.ts","sourceRoot":"","sources":["../../src/pagination/materializePagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEtD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAE5D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAGvE,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,4BAA4B,CAAC;CACtC,CAAA;AAED,eAAO,MAAM,0BAA0B,GAAI,KAAK,EAC9C,UAAU,uBAAuB,EACjC,cAAc,KAAK,EAAE,EACrB,SAAS,4BAA4B,KACpC,kBAAkB,CAAC,KAAK,CA4B1B,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { CompiledMongoPagination } from './compileMongoPagination';
2
+ type MongoQueryLike = {
3
+ where: (delta: Record<string, unknown>) => unknown;
4
+ sort: (sort: Record<string, 1 | -1>) => unknown;
5
+ limit: (limit: number) => unknown;
6
+ };
7
+ export declare const MongoAdapter: {
8
+ applyPagination: <TQuery extends MongoQueryLike>(query: TQuery, compiled: CompiledMongoPagination) => TQuery;
9
+ };
10
+ export {};
11
+ //# sourceMappingURL=mongoAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongoAdapter.d.ts","sourceRoot":"","sources":["../../src/pagination/mongoAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAGvE,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IACnD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;IAChD,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC,CAAA;AAED,eAAO,MAAM,YAAY;sBACL,MAAM,SAAS,cAAc,SAAS,MAAM,YAAY,uBAAuB,KAAG,MAAM;CAM3G,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { default as mongoose } from '../../../vite/node_modules/mongoose';
2
+ import { PaginationResponse, PaginationSpec } from '../../../api/src';
3
+ import { PaginationCursorCodecOptions } from './cursor';
4
+ declare const mongoPaginationPluginBrand: unique symbol;
5
+ export type MongoPaginationPluginOptions = {
6
+ cursor: PaginationCursorCodecOptions;
7
+ };
8
+ type QueryResultNode<TResult> = TResult extends Array<infer TNode> ? TNode : unknown;
9
+ declare module '../../../vite/node_modules/mongoose' {
10
+ interface Query<ResultType, DocType, THelpers = {}, RawDocType = unknown, QueryOp = "find", TDocOverrides = Record<string, never>> {
11
+ [mongoPaginationPluginBrand]?: [DocType, THelpers, RawDocType, QueryOp, TDocOverrides];
12
+ paginate: (pagination?: PaginationSpec, options?: MongoPaginationPluginOptions) => Promise<PaginationResponse<QueryResultNode<ResultType>>>;
13
+ }
14
+ }
15
+ export declare const mongoPaginationPlugin: (schema: mongoose.Schema, pluginOptions?: MongoPaginationPluginOptions) => void;
16
+ export {};
17
+ //# sourceMappingURL=mongoPaginationPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongoPaginationPlugin.d.ts","sourceRoot":"","sources":["../../src/pagination/mongoPaginationPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAC/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAEtE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAI5D,OAAO,CAAC,MAAM,0BAA0B,EAAE,OAAO,MAAM,CAAA;AAqBvD,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,4BAA4B,CAAC;CACtC,CAAA;AAED,KAAK,eAAe,CAAC,OAAO,IAAI,OAAO,SAAS,KAAK,CAAC,MAAM,KAAK,CAAC,GAAG,KAAK,GAAG,OAAO,CAAA;AAEpF,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,KAAK,CACb,UAAU,EACV,OAAO,EACP,QAAQ,GAAG,EAAE,EACb,UAAU,GAAG,OAAO,EACpB,OAAO,GAAG,MAAM,EAChB,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;QAErC,CAAC,0BAA0B,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QACvF,QAAQ,EAAE,CACR,UAAU,CAAC,EAAE,cAAc,EAC3B,OAAO,CAAC,EAAE,4BAA4B,KACnC,OAAO,CAAC,kBAAkB,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;KAC/D;CACF;AAED,eAAO,MAAM,qBAAqB,GAAI,QAAQ,QAAQ,CAAC,MAAM,EAAE,gBAAgB,4BAA4B,KAAG,IAc7G,CAAA"}
@@ -0,0 +1,11 @@
1
+ import { PaginationDirection, PaginationOrder, PaginationSpec } from '../../../api/src';
2
+ export type NormalizedPaginationSpec = Omit<PaginationSpec, "direction" | "sort" | "limit"> & {
3
+ direction: PaginationDirection;
4
+ limit: number;
5
+ sort: Array<{
6
+ field: string;
7
+ order: PaginationOrder;
8
+ }>;
9
+ };
10
+ export declare const normalizePaginationSpec: (spec: PaginationSpec) => NormalizedPaginationSpec;
11
+ //# sourceMappingURL=normalizeSpec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeSpec.d.ts","sourceRoot":"","sources":["../../src/pagination/normalizeSpec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAQxF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG;IAC5F,SAAS,EAAE,mBAAmB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,eAAe,CAAC;KACxB,CAAC,CAAC;CACJ,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,MAAM,cAAc,KAAG,wBAgC9D,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { PaginationResponse, PaginationSpec } from '../../../api/src';
2
+ import { PaginationCursorCodecOptions } from './cursor';
3
+ type MongoPaginateQueryLike<TNode> = {
4
+ where: (delta: Record<string, unknown>) => unknown;
5
+ sort: (sort: Record<string, 1 | -1>) => unknown;
6
+ limit: (limit: number) => unknown;
7
+ exec: () => Promise<TNode[]>;
8
+ };
9
+ export type PaginateMongoQueryOptions = {
10
+ cursor: PaginationCursorCodecOptions;
11
+ };
12
+ export declare const paginateMongoQuery: <TNode>(query: MongoPaginateQueryLike<TNode>, pagination: PaginationSpec, options: PaginateMongoQueryOptions) => Promise<PaginationResponse<TNode>>;
13
+ export {};
14
+ //# sourceMappingURL=paginateMongoQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paginateMongoQuery.d.ts","sourceRoot":"","sources":["../../src/pagination/paginateMongoQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAEtE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAM5D,KAAK,sBAAsB,CAAC,KAAK,IAAI;IACnC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IACnD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;IAChD,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,IAAI,EAAE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;CAC9B,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,4BAA4B,CAAC;CACtC,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAU,KAAK,EAC5C,OAAO,sBAAsB,CAAC,KAAK,CAAC,EACpC,YAAY,cAAc,EAC1B,SAAS,yBAAyB,KACjC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAWnC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"registerModels.d.ts","sourceRoot":"","sources":["../src/registerModels.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAa/B,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAwE3C,eAAO,MAAM,cAAc,GAAI,SAAS,YAAY,SAInD,CAAA;AAED,eAAO,MAAM,mBAAmB,sEAK/B,CAAA;AAED,YAAY,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"registerModels.d.ts","sourceRoot":"","sources":["../src/registerModels.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAiB/B,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAwF3C,eAAO,MAAM,cAAc,GAAI,SAAS,YAAY,SAInD,CAAA;AAED,eAAO,MAAM,mBAAmB,sEAK/B,CAAA;AAED,YAAY,EAAE,YAAY,EAAE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/db",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"