@classytic/mongokit 3.29.0 → 3.31.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.
Files changed (40) hide show
  1. package/README.md +43 -0
  2. package/dist/{PaginationEngine-CczbvCAq.mjs → PaginationEngine-CuONvwCM.mjs} +21 -1
  3. package/dist/{create-repository-D4ZU94b3.d.mts → Repository-BAzymuOz.d.mts} +31 -58
  4. package/dist/{Repository-DThvFCaV.mjs → Repository-BQ3FEnOa.mjs} +352 -56
  5. package/dist/actions/index.d.mts +1 -1
  6. package/dist/actions/index.mjs +1 -1
  7. package/dist/adapter/index.mjs +1 -1
  8. package/dist/{adapter-CLvUHiTW.mjs → adapter-BitHg-CN.mjs} +1 -1
  9. package/dist/ai/index.d.mts +1 -1
  10. package/dist/better-auth/index.d.mts +13 -0
  11. package/dist/better-auth/index.mjs +76 -6
  12. package/dist/{create-repository-uhKt_oUT.mjs → create-repository-BOTVMsIg.mjs} +3 -2
  13. package/dist/create-repository-mEdfH9sz.d.mts +54 -0
  14. package/dist/{index-DceInCTX.d.mts → index-Beyr1r4d.d.mts} +1 -1
  15. package/dist/index.d.mts +305 -43
  16. package/dist/index.mjs +118 -11
  17. package/dist/kernel-conformance/index.d.mts +217 -0
  18. package/dist/kernel-conformance/index.mjs +603 -0
  19. package/dist/method-registry.plugin-DblM7V6j.mjs +112 -0
  20. package/dist/model-registry-ClKtWiHa.mjs +67 -0
  21. package/dist/{mongooseToJsonSchema-CByt1VhQ.mjs → mongooseToJsonSchema-DKbnKduN.mjs} +14 -3
  22. package/dist/{multi-tenant.plugin-DVyVGREb.d.mts → multi-tenant.plugin-2VtSSm1Y.d.mts} +1 -1
  23. package/dist/{operations-C8IcCHF9.d.mts → operations-CuzX9m2Z.d.mts} +42 -3
  24. package/dist/outbox/index.d.mts +63 -0
  25. package/dist/outbox/index.mjs +239 -0
  26. package/dist/pagination/PaginationEngine.mjs +1 -1
  27. package/dist/{plugin-options-vFufQZMZ.d.mts → plugin-options-BsFH0sTi.d.mts} +1 -1
  28. package/dist/plugins/index.d.mts +4 -4
  29. package/dist/plugins/index.mjs +3 -2
  30. package/dist/{repository-DCHRWRXw.d.mts → repository-Q16ErTu8.d.mts} +19 -1
  31. package/dist/testkit/index.d.mts +8 -1
  32. package/dist/testkit/index.mjs +1 -1
  33. package/dist/{timestamp.plugin-CFL6LlYZ.mjs → timestamp.plugin-i9Qxm6kn.mjs} +32 -143
  34. package/dist/{to-plain-DkF5px0R.d.mts → to-plain-BuX1RUZV.d.mts} +12 -4
  35. package/dist/{update-CI2NdAW6.mjs → update-DAO5STDv.mjs} +62 -7
  36. package/dist/utils/index.d.mts +2 -2
  37. package/dist/utils/index.mjs +1 -1
  38. package/dist/{validation-chain.plugin-BLecaKa0.d.mts → validation-chain.plugin-BSshLJTA.d.mts} +3 -3
  39. package/dist/{validation-chain.plugin-CwOcUJC1.mjs → validation-chain.plugin-CpcB6dOd.mjs} +4 -3
  40. package/package.json +193 -187
package/README.md CHANGED
@@ -430,6 +430,49 @@ Atlas **`$search` / `$vectorSearch`** indexes are Atlas-only and don't exist on
430
430
 
431
431
  All accept `{ replset?, dbName?, uri? }`.
432
432
 
433
+ ## Kernel Conformance — `@classytic/mongokit/kernel-conformance`
434
+
435
+ An **executable contract suite** for domain kernels built on `defineModels()`. A kernel that
436
+ follows the Describe → Bind → Verify → Serve → Close lifecycle wires this into its own test
437
+ directory; passing it IS conformance.
438
+
439
+ It asserts what ordinary kernel tests do not: that `defineX(shape)` registers nothing, that
440
+ `bind` registers exactly the declared models on the SUPPLIED connection and performs no index
441
+ I/O, that a second bind throws `ModelCollisionError`, that a host-supplied event transport is
442
+ never closed while an internally-created one is, that `close()` is idempotent, that no legacy
443
+ `createX`/`destroy`/`dispose` surface survives, and that a failing index sync REJECTS rather
444
+ than being swallowed.
445
+
446
+ **The test runner is injected, never imported** — mongokit takes no dependency (not even an
447
+ optional peer) on vitest, and the suite runs unmodified under any runner that exposes
448
+ `describe`/`it`. Assertions are internal, so no `expect` implementation is required.
449
+
450
+ ```ts
451
+ import mongoose from 'mongoose';
452
+ import { describe, it } from 'vitest';
453
+ import { describeKernelConformance } from '@classytic/mongokit/kernel-conformance';
454
+ import * as kernel from '../../src/index.js';
455
+
456
+ describeKernelConformance({
457
+ name: 'party',
458
+ runner: { describe, it },
459
+ // A FACTORY — purity is only observable if the suite can call defineX itself.
460
+ blueprint: () => kernel.defineParty({ multiTenant: false, autoIndex: false }),
461
+ // Never connected: every check is registry-level, so no server is needed.
462
+ connect: async () => mongoose.createConnection(),
463
+ bind: (bp, conn, ctx) => bp.bind(conn, { eventTransport: ctx.transport }),
464
+ expectedModelNames: ['Party'],
465
+ moduleExports: kernel,
466
+ // Opt-outs are explicit, typed, and REQUIRE a reason; each one still emits a
467
+ // visible `[SKIPPED] <id> — <reason>` test. A silent skip is the bug this suite exists
468
+ // to prevent.
469
+ skip: [{ check: 'bind-verifies-requirements', reason: '…' }],
470
+ });
471
+ ```
472
+
473
+ Check ids are exported as `KERNEL_CONFORMANCE_CHECKS` (a `const` tuple, so `skip` is a typed
474
+ union — a mistyped id is a compile error, not an ineffective skip).
475
+
433
476
  ## License
434
477
 
435
478
  MIT
@@ -636,7 +636,27 @@ var PaginationEngine = class {
636
636
  hasNext = data.length > sanitizedLimit;
637
637
  if (hasNext) data.pop();
638
638
  } else hasNext = sanitizedPage < totalPages;
639
- const warning = shouldWarnDeepPagination(sanitizedPage, this.config.deepPageThreshold) ? `Deep pagination (page ${sanitizedPage}). Consider getAll({ after, sort, limit }) for better performance.` : void 0;
639
+ /**
640
+ * A CLAMPED limit is reported, not swallowed.
641
+ *
642
+ * `validateLimit` caps silently by design — an over-ask is benign and rejecting it
643
+ * would be worse (that contradiction cost a day: the querystring schema's `maximum`
644
+ * made the parser's documented clamp unreachable, so `?limit=200` 400'd and two UI
645
+ * callers rendered the failure as an empty list).
646
+ *
647
+ * But clamping quietly has its own cost. A caller asking for 1000 and receiving 100
648
+ * gets an arbitrary slice with no signal — which is exactly how a bank-account
649
+ * picker rendered "No accounts found" against 696 accounts, because the rows it
650
+ * wanted were outside the first page. `limit` in the response already carries the
651
+ * effective value; nobody compares it. A warning is the channel that is actually
652
+ * read.
653
+ *
654
+ * Deep-pagination takes precedence when both apply: it is the more actionable of
655
+ * the two, and stacking warnings turns a signal into noise.
656
+ */
657
+ const requestedLimit = Number(limit);
658
+ const wasClamped = Number.isFinite(requestedLimit) && requestedLimit > sanitizedLimit;
659
+ const warning = shouldWarnDeepPagination(sanitizedPage, this.config.deepPageThreshold) ? `Deep pagination (page ${sanitizedPage}). Consider getAll({ after, sort, limit }) for better performance.` : wasClamped ? `Requested limit ${Math.floor(requestedLimit)} exceeds this repository's cap; returning ${sanitizedLimit}. Filter server-side or sort deterministically — the returned page is otherwise an arbitrary slice.` : void 0;
640
660
  return {
641
661
  method: "offset",
642
662
  data,
@@ -1,10 +1,8 @@
1
- import { _ as WithTransactionOptions, a as DeleteManyResult, f as OperationOptions, g as UpdateOptions, h as UpdateManyResult, i as CreateOptions, l as LookupPopulateOptions, m as SessionOptions, o as DeleteResult, p as ReadOptions, r as CacheableOptions, s as FindOneAndUpdateOptions, t as AggregateOptions, u as LookupPopulateResult, v as LookupBuilder, y as LookupOptions } from "./operations-C8IcCHF9.mjs";
1
+ import { _ as WithTransactionOptions, a as DeleteManyResult, f as OperationOptions, g as UpdateOptions, h as UpdateManyResult, i as CreateOptions, l as LookupPopulateOptions, m as SessionOptions, o as DeleteResult, p as ReadOptions, r as CacheableOptions, s as FindOneAndUpdateOptions, t as AggregateOptions, u as LookupPopulateResult, v as LookupBuilder, y as LookupOptions } from "./operations-CuzX9m2Z.mjs";
2
2
  import { a as PopulateSpec, i as ObjectId, l as SortSpec$1, o as ReadPreferenceType, s as SelectSpec } from "./core-62oo3N1C.mjs";
3
3
  import { c as PaginationConfig, r as CollationOptions, t as AggregatePaginationOptions } from "./pagination-jSUdrACK.mjs";
4
- import { _ as RepositoryOptions, d as PluginType, f as PrioritizedHook, o as Middleware, p as RepositoryContext } from "./repository-DCHRWRXw.mjs";
5
- import { T as AuditTrailOptions, _ as RepositoryCachePluginOptions, i as CustomIdOptions, t as MultiTenantOptions } from "./multi-tenant.plugin-DVyVGREb.mjs";
4
+ import { _ as RepositoryOptions, d as PluginType, f as PrioritizedHook, o as Middleware, p as RepositoryContext } from "./repository-Q16ErTu8.mjs";
6
5
  import { t as PaginationEngine } from "./PaginationEngine-CBS1XGzp.mjs";
7
- import { i as Logger, o as SoftDeleteOptions } from "./plugin-options-vFufQZMZ.mjs";
8
6
  import { AggPaginationRequest, AggRequest, AggResult, ArchiveOptions, ArchiveResult, ArchiveSink, ChangeEvent, FilterInput, KeysetAggPaginationResult, QueryOptions, RepoCapabilities, RepositoryBase, TenantPurgeOptions, TenantPurgeResult, TenantPurgeStrategy, WatchOptions } from "@classytic/repo-core/repository";
9
7
  import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
10
8
  import { Filter } from "@classytic/repo-core/filter";
@@ -526,8 +524,29 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
526
524
  * Runtime capability descriptor required by `StandardRepo<TDoc>`
527
525
  * (repo-core 0.6.0). Hosts feature-detect once at boot instead of
528
526
  * try/catching `UnsupportedOperationError` per call.
529
- */
530
- readonly capabilities: RepoCapabilities;
527
+ *
528
+ * **OBSERVED, not declared** (changed 2026-08-05). Until then this was a
529
+ * class field pointing at the static `MONGOKIT_CAPABILITIES`, so
530
+ * `transactions` read `true` for every mongoose-backed repository —
531
+ * including one pointed at a standalone `mongod`, which cannot run a
532
+ * transaction at all. Every kernel boot gate that reads it (wallet, party,
533
+ * invoice, purchase, flow, …) was therefore unfalsifiable decoration
534
+ * (AGENTS.md FAIL LOUD rule 4).
535
+ *
536
+ * Now it derives the deployment-dependent flags (`transactions`,
537
+ * `nestedTransactions`, `changeStreams`) from THIS model's connection and
538
+ * its live SDAM topology. A getter rather than a constructor snapshot on
539
+ * purpose: a repository built before `mongoose.connect()` resolves upgrades
540
+ * its own answer the moment the connection is up, with no invalidation step.
541
+ * When the topology cannot be read the answer is `transactions: false` +
542
+ * `transactionsResolution: 'unknown'` — fail closed, never an optimistic
543
+ * yes (rule 3). See `capabilities.ts` and `probeMongoCapabilities`.
544
+ *
545
+ * `options.capabilities` overrides the whole descriptor for hosts/tests that
546
+ * genuinely know better (a proxied connection, a non-mongoose driver seam).
547
+ */
548
+ get capabilities(): RepoCapabilities;
549
+ private readonly _capabilitiesOverride;
531
550
  readonly Model: Model<TDoc>;
532
551
  /**
533
552
  * Mongoose model name. Duplicated on the instance for BC — arc / catalog /
@@ -927,8 +946,9 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
927
946
  /**
928
947
  * Update a document by id and return the post-update doc.
929
948
  *
930
- * Compiles to `Model.findOneAndUpdate(filter, { $set: data }, { new:
931
- * true, runValidators: true })` under the hood and routes through the
949
+ * Compiles to `Model.findOneAndUpdate(filter, { $set: data },
950
+ * { returnDocument: 'after', runValidators: true })` under the hood and
951
+ * routes through the
932
952
  * full plugin pipeline — `multiTenantPlugin` injects the tenant scope,
933
953
  * `auditTrailPlugin` records the change, `cachePlugin` invalidates,
934
954
  * `softDeletePlugin` etc. all compose. Returns `null` on miss (the
@@ -943,7 +963,7 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
943
963
  * const doc = await CampaignModel.findOneAndUpdate(
944
964
  * { _id: id, organizationId: ctx.orgId }, // hand-rolled tenant filter
945
965
  * { $set: patch, $currentDate: { updatedAt: 1 } },
946
- * { new: true, runValidators: true }
966
+ * { returnDocument: 'after', runValidators: true }
947
967
  * );
948
968
  * return doc ? toEntity(doc) : null;
949
969
  *
@@ -972,7 +992,7 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
972
992
  * const doc = await CampaignModel.findOneAndUpdate(
973
993
  * { _id: id, organizationId: ctx.orgId, status: 'pending' },
974
994
  * { $set: { status: 'sent', sentAt: new Date() } },
975
- * { new: true }
995
+ * { returnDocument: 'after' }
976
996
  * );
977
997
  * if (!doc) throw new ConcurrentTransitionError(id);
978
998
  *
@@ -1826,51 +1846,4 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
1826
1846
  _handleError(error: Error): HttpError;
1827
1847
  }
1828
1848
  //#endregion
1829
- //#region src/create-repository.d.ts
1830
- /**
1831
- * Audit config — either a `Logger` (routes to `auditLogPlugin`, which
1832
- * writes structured log lines through your logger) or an
1833
- * `AuditTrailOptions` object (routes to `auditTrailPlugin`, which
1834
- * persists a queryable compliance ledger in its own collection).
1835
- *
1836
- * Discrimination: a value carrying any of the `Logger` function members
1837
- * (`info` / `warn` / `error` / `debug`) is treated as a logger; anything
1838
- * else is treated as `AuditTrailOptions` (all of whose members are
1839
- * data-shaped except `metadata` / `onError`, which don't overlap with
1840
- * the logger member names).
1841
- */
1842
- type AuditConfig = Logger | AuditTrailOptions;
1843
- /**
1844
- * Declarative repository config. Every key is optional; a feature's
1845
- * plugin is installed only when its key is present. The remaining
1846
- * `RepositoryOptions` keys (`idField`, `hooks`, `schema`, `updateSchema`,
1847
- * `events`, `requirePlugins`, ...) pass through to the `Repository`
1848
- * constructor unchanged.
1849
- */
1850
- interface CreateRepositoryConfig extends RepositoryOptions {
1851
- /** Multi-tenant scoping — forwarded to `multiTenantPlugin`. */
1852
- tenant?: MultiTenantOptions;
1853
- /** Soft-delete — `true` for defaults, or `SoftDeleteOptions`. */
1854
- softDelete?: boolean | SoftDeleteOptions;
1855
- /** createdAt/updatedAt stamping — `timestampPlugin` takes no options. */
1856
- timestamps?: boolean;
1857
- /** Batch ops (`updateMany`/`deleteMany`/`bulkWrite`) via `batchOperationsPlugin`. */
1858
- batch?: boolean;
1859
- /** Read-through cache — forwarded to `cachePlugin`. */
1860
- cache?: RepositoryCachePluginOptions;
1861
- /** Audit — `Logger` → `auditLogPlugin`; `AuditTrailOptions` → `auditTrailPlugin`. */
1862
- audit?: AuditConfig;
1863
- /** Custom ID generation — forwarded to `customIdPlugin`. */
1864
- customId?: CustomIdOptions;
1865
- /** Extra plugins appended AFTER the canonical stack. */
1866
- plugins?: PluginType[];
1867
- /** Pagination engine config (defaultLimit, maxLimit, ...). */
1868
- pagination?: PaginationConfig;
1869
- }
1870
- /**
1871
- * Build a `Repository` from a declarative config. See module JSDoc for
1872
- * ordering guarantees and an example.
1873
- */
1874
- declare function createRepository<TDoc>(model: Model<TDoc, any, any, any>, config?: CreateRepositoryConfig): Repository<TDoc>;
1875
- //#endregion
1876
- export { Repository as a, HOOK_PRIORITY as i, CreateRepositoryConfig as n, TransitionMachine as o, createRepository as r, AggregationBuilder as s, AuditConfig as t };
1849
+ export { AggregationPlan as a, SortOrder as c, AggregationBuilder as i, SortSpec as l, Repository as n, GroupSpec as o, TransitionMachine as r, ProjectionSpec as s, HOOK_PRIORITY as t, VectorSearchOptions as u };