@selvajs/platform 0.14.3-beta.0 → 0.15.0-beta.2

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 (154) hide show
  1. package/LICENSE +1 -1
  2. package/dist/computeServer/index.d.ts +1 -1
  3. package/dist/computeServer/index.d.ts.map +1 -1
  4. package/dist/computeServer/interface.d.ts +29 -1
  5. package/dist/computeServer/interface.d.ts.map +1 -1
  6. package/dist/computeServer/types.d.ts +12 -1
  7. package/dist/computeServer/types.d.ts.map +1 -1
  8. package/dist/computeServer/types.js.map +1 -1
  9. package/dist/data/index.d.ts +2 -1
  10. package/dist/data/index.d.ts.map +1 -1
  11. package/dist/data/index.js +1 -1
  12. package/dist/data/index.js.map +1 -1
  13. package/dist/data/interface.d.ts +69 -8
  14. package/dist/data/interface.d.ts.map +1 -1
  15. package/dist/data/interface.js +7 -1
  16. package/dist/data/interface.js.map +1 -1
  17. package/dist/definitions/interface.d.ts +16 -1
  18. package/dist/definitions/interface.d.ts.map +1 -1
  19. package/dist/definitions/schemas.d.ts +1 -0
  20. package/dist/definitions/schemas.d.ts.map +1 -1
  21. package/dist/definitions/schemas.js +8 -0
  22. package/dist/definitions/schemas.js.map +1 -1
  23. package/dist/definitions/types.d.ts +24 -1
  24. package/dist/definitions/types.d.ts.map +1 -1
  25. package/dist/events/index.d.ts +1 -1
  26. package/dist/events/index.d.ts.map +1 -1
  27. package/dist/events/index.js +1 -1
  28. package/dist/events/index.js.map +1 -1
  29. package/dist/events/interface.d.ts +30 -0
  30. package/dist/events/interface.d.ts.map +1 -1
  31. package/dist/events/interface.js +7 -0
  32. package/dist/events/interface.js.map +1 -1
  33. package/dist/index.d.ts +9 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +5 -2
  36. package/dist/index.js.map +1 -1
  37. package/dist/logging/interface.d.ts +82 -0
  38. package/dist/logging/interface.d.ts.map +1 -0
  39. package/dist/logging/interface.js +33 -0
  40. package/dist/logging/interface.js.map +1 -0
  41. package/dist/metrics/interface.d.ts +6 -0
  42. package/dist/metrics/interface.d.ts.map +1 -1
  43. package/dist/metrics/interface.js.map +1 -1
  44. package/dist/organizations/__tests__/slug-schema.test.d.ts +2 -0
  45. package/dist/organizations/__tests__/slug-schema.test.d.ts.map +1 -0
  46. package/dist/organizations/__tests__/slug-schema.test.js +38 -0
  47. package/dist/organizations/__tests__/slug-schema.test.js.map +1 -0
  48. package/dist/organizations/index.d.ts +1 -1
  49. package/dist/organizations/index.d.ts.map +1 -1
  50. package/dist/organizations/index.js +1 -1
  51. package/dist/organizations/index.js.map +1 -1
  52. package/dist/organizations/schemas.d.ts +16 -2
  53. package/dist/organizations/schemas.d.ts.map +1 -1
  54. package/dist/organizations/schemas.js +32 -2
  55. package/dist/organizations/schemas.js.map +1 -1
  56. package/dist/solveCache/interface.d.ts +86 -0
  57. package/dist/solveCache/interface.d.ts.map +1 -0
  58. package/dist/solveCache/interface.js +12 -0
  59. package/dist/solveCache/interface.js.map +1 -0
  60. package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
  61. package/dist/testing/suites/definitionStoreSuite.js +57 -0
  62. package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
  63. package/dist/testing/suites/eventSinkSuite.js +1 -0
  64. package/dist/testing/suites/eventSinkSuite.js.map +1 -1
  65. package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
  66. package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
  67. package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
  68. package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
  69. package/dist/testing/suites/solveMetricSinkSuite.d.ts +5 -0
  70. package/dist/testing/suites/solveMetricSinkSuite.d.ts.map +1 -1
  71. package/dist/testing/suites/solveMetricSinkSuite.js +17 -8
  72. package/dist/testing/suites/solveMetricSinkSuite.js.map +1 -1
  73. package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
  74. package/dist/testing/suites/storageProviderSuite.js +23 -2
  75. package/dist/testing/suites/storageProviderSuite.js.map +1 -1
  76. package/package.json +9 -24
  77. package/src/access/index.ts +0 -23
  78. package/src/access/rules.ts +0 -276
  79. package/src/auth/index.ts +0 -8
  80. package/src/auth/interface.ts +0 -279
  81. package/src/auth/types.ts +0 -32
  82. package/src/bindings/index.ts +0 -2
  83. package/src/bindings/interface.ts +0 -61
  84. package/src/computeServer/index.ts +0 -28
  85. package/src/computeServer/interface.ts +0 -73
  86. package/src/computeServer/secrets.ts +0 -102
  87. package/src/computeServer/types.ts +0 -74
  88. package/src/computeServer/utils.ts +0 -112
  89. package/src/config.ts +0 -99
  90. package/src/context.ts +0 -80
  91. package/src/data/index.ts +0 -1
  92. package/src/data/interface.ts +0 -70
  93. package/src/definitions/index.ts +0 -26
  94. package/src/definitions/interface.ts +0 -87
  95. package/src/definitions/paths.ts +0 -31
  96. package/src/definitions/schemas.ts +0 -64
  97. package/src/definitions/types.ts +0 -111
  98. package/src/errors/interface.ts +0 -45
  99. package/src/errors.ts +0 -16
  100. package/src/events/audit.ts +0 -67
  101. package/src/events/index.ts +0 -9
  102. package/src/events/interface.ts +0 -102
  103. package/src/index.ts +0 -247
  104. package/src/invites/index.ts +0 -2
  105. package/src/invites/interface.ts +0 -38
  106. package/src/invites/types.ts +0 -39
  107. package/src/metrics/interface.ts +0 -86
  108. package/src/organizations/index.ts +0 -23
  109. package/src/organizations/interface.ts +0 -68
  110. package/src/organizations/paths.ts +0 -45
  111. package/src/organizations/schemas.ts +0 -61
  112. package/src/organizations/types.ts +0 -41
  113. package/src/pagination.ts +0 -45
  114. package/src/permissions/index.ts +0 -3
  115. package/src/permissions/interface.ts +0 -60
  116. package/src/permissions/types.ts +0 -21
  117. package/src/platformProjects/index.ts +0 -3
  118. package/src/platformProjects/interface.ts +0 -27
  119. package/src/platformProjects/schemas.ts +0 -13
  120. package/src/platformProjects/types.ts +0 -22
  121. package/src/projects/index.ts +0 -4
  122. package/src/projects/interface.ts +0 -58
  123. package/src/projects/schemas.ts +0 -28
  124. package/src/projects/types.ts +0 -36
  125. package/src/shareLinks/index.ts +0 -5
  126. package/src/shareLinks/interface.ts +0 -35
  127. package/src/shareLinks/schemas.ts +0 -21
  128. package/src/shareLinks/types.ts +0 -34
  129. package/src/storage/assetClasses.ts +0 -134
  130. package/src/storage/cacheBust.ts +0 -26
  131. package/src/storage/image.ts +0 -82
  132. package/src/storage/index.ts +0 -12
  133. package/src/storage/interface.ts +0 -27
  134. package/src/testing/index.ts +0 -47
  135. package/src/testing/suites/authProviderSuite.ts +0 -207
  136. package/src/testing/suites/computeServerStoreSuite.ts +0 -195
  137. package/src/testing/suites/definitionStoreSuite.ts +0 -630
  138. package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
  139. package/src/testing/suites/eventSinkSuite.ts +0 -379
  140. package/src/testing/suites/helpers.ts +0 -87
  141. package/src/testing/suites/inviteStoreSuite.ts +0 -143
  142. package/src/testing/suites/orgStoreSuite.ts +0 -429
  143. package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
  144. package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
  145. package/src/testing/suites/projectStoreSuite.ts +0 -381
  146. package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
  147. package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
  148. package/src/testing/suites/storageProviderSuite.ts +0 -196
  149. package/src/testing/suites/userProfileStoreSuite.ts +0 -134
  150. package/src/userProfile/index.ts +0 -3
  151. package/src/userProfile/interface.ts +0 -48
  152. package/src/userProfile/types.ts +0 -24
  153. package/src/utils/audit.ts +0 -46
  154. package/src/utils/index.ts +0 -2
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  export { PlatformPermissionSchema, ALL_PLATFORM_PERMISSIONS } from './permissions/types.js';
3
3
  export { emptyProfile } from './userProfile/types.js';
4
4
  export { orgPaths } from './organizations/paths.js';
5
- export { OrgRoleSchema, OrgPermissionSchema, OrgAssetKindSchema, ALL_ORG_ASSET_KINDS, SlugSchema, CreateOrgSchema, UpdateOrgSchema, ALL_ORG_PERMISSIONS, DEFAULT_ORG_PERMISSIONS, OWNER_ADMIN_ONLY_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from './organizations/schemas.js';
5
+ export { OrgRoleSchema, OrgPermissionSchema, OrgAssetKindSchema, ALL_ORG_ASSET_KINDS, SlugSchema, RESERVED_SLUGS, CreateOrgSchema, UpdateOrgSchema, ALL_ORG_PERMISSIONS, DEFAULT_ORG_PERMISSIONS, OWNER_ADMIN_ONLY_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from './organizations/schemas.js';
6
6
  export { ProjectVisibilitySchema, ProjectRoleSchema, validateProjectFlags } from './projects/schemas.js';
7
7
  export { PlatformProjectGrantSchema, PlatformProjectGranteeTypeSchema } from './platformProjects/schemas.js';
8
8
  export { GH_EXTENSIONS, COVER_IMAGE_EXTENSIONS, ALLOWED_UPLOAD_EXTENSIONS, COVER_IMAGE_CONTENT_TYPES } from './definitions/types.js';
@@ -13,12 +13,15 @@ export { CreateShareLinkInputSchema } from './shareLinks/schemas.js';
13
13
  export { IMAGE_MAX_WIDTH, IMAGE_WEBP_QUALITY, isImageUpload, toWebpPath, transcodeImageIfNeeded } from './storage/image.js';
14
14
  export { ASSET_CLASSES, classifyAssetPath, isPublicAssetPath } from './storage/assetClasses.js';
15
15
  export { withCacheBust } from './storage/cacheBust.js';
16
+ export { ERASED_ACTOR_ID } from './data/interface.js';
16
17
  export { isPlatformServer, isOrgServer } from './computeServer/types.js';
17
18
  export { serversVisibleTo, defaultServerIdFor, resolveServerForOrg, findServerById, platformServers, orgServersFor } from './computeServer/utils.js';
18
19
  export { canView, canSolve, canEdit, canManage, canEditProjectSettings, canChangeVisibilityToPublic, canEditDefinition, canReclaim, canCreateProject, checkOwnerRemoval, withAdminBypass } from './access/rules.js';
19
- export { actorFrom, NoopEventSink } from './events/interface.js';
20
+ export { actorFrom, NoopEventSink, AUDIT_EVENT_VERSION } from './events/interface.js';
20
21
  export { NoopSolveMetricSink } from './metrics/interface.js';
22
+ export { NoopSolveResultCache } from './solveCache/interface.js';
21
23
  export { NoopErrorReporter } from './errors/interface.js';
24
+ export { NoopLogger } from './logging/interface.js';
22
25
  export { NoopBindingResolver } from './bindings/interface.js';
23
26
  export { auditUpdate, auditSoftDelete } from './utils/audit.js';
24
27
  export { SYSTEM_CONTEXT, hasPermission, requireActingOrg } from './context.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAgB9F,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAM5F,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAKtD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQpD,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,MAAM,4BAA4B,CAAC;AAUpC,OAAO,EACN,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACN,0BAA0B,EAC1B,gCAAgC,EAChC,MAAM,+BAA+B,CAAC;AAavC,OAAO,EACN,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EACN,uBAAuB,EACvB,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,UAAU,EACV,UAAU,EACV,MAAM,0BAA0B,CAAC;AAOlC,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAMrE,OAAO,EACN,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,sBAAsB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAOhG,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAgBvD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAazE,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,aAAa,EACb,MAAM,0BAA0B,CAAC;AAclC,OAAO,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,2BAA2B,EAC3B,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAajE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAM7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAM1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAM9D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMhE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG/E,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AASrE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAgB9F,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAM5F,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAKtD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQpD,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,MAAM,4BAA4B,CAAC;AAUpC,OAAO,EACN,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACN,0BAA0B,EAC1B,gCAAgC,EAChC,MAAM,+BAA+B,CAAC;AAavC,OAAO,EACN,aAAa,EACb,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EACN,uBAAuB,EACvB,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,UAAU,EACV,UAAU,EACV,MAAM,0BAA0B,CAAC;AAOlC,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAMrE,OAAO,EACN,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,sBAAsB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAOhG,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAMvD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAWtD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAazE,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,aAAa,EACb,MAAM,0BAA0B,CAAC;AAclC,OAAO,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,2BAA2B,EAC3B,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAatF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAU7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAMjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAM1D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAMpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAM9D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMhE,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG/E,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AASrE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Structured logging seam. Mirrors the `IErrorReporter` split next door: the
3
+ * interface + a safe default live here, the real backend (pino) ships from
4
+ * `@selvajs/server` so nothing in `@selvajs/platform` gains a logging
5
+ * dependency and browser-side consumers never pull a Node logger into a bundle.
6
+ *
7
+ * Why an interface rather than importing pino directly at call sites:
8
+ * - `@selvajs/platform` and `@selvajs/server` are consumed as libraries. A
9
+ * hard pino import would impose a logger (and its transport config) on every
10
+ * embedder, including ones that already own their logging stack.
11
+ * - Tests capture log output by passing a fake, with no transport to stub.
12
+ * - Call sites stay honest: they describe WHAT happened and hand over fields;
13
+ * formatting, redaction and routing are the backend's business.
14
+ */
15
+ /**
16
+ * Structured fields attached to one log record. Deliberately `unknown` values
17
+ * rather than `string`: a field's whole point is to survive as typed JSON
18
+ * (numbers stay numbers, so a log backend can range-query them) instead of
19
+ * being flattened into a message string the way the old `console.log`
20
+ * template-literal calls did.
21
+ *
22
+ * Never put secrets here — session tokens, API keys, refresh tokens, raw share
23
+ * tokens. Log records routinely leave the box (stdout → collector → third-party
24
+ * index) and are retained far longer than the credential's own lifetime.
25
+ */
26
+ export type LogFields = Record<string, unknown>;
27
+ /**
28
+ * Severity levels, ordered. Chosen to match pino's standard set so the backend
29
+ * is a thin pass-through and operators get the level names they expect.
30
+ *
31
+ * Guidance, since the old `console.*` calls used levels inconsistently:
32
+ * - `debug` — detail only useful when actively diagnosing; off in production.
33
+ * - `info` — a thing happened that an operator would want in the record
34
+ * (boot, config resolved, request completed).
35
+ * - `warn` — degraded or suspicious, but handled; the system continues.
36
+ * - `error` — an operation failed. Pair with `IErrorReporter` when it's
37
+ * genuinely unexpected: logs are for the operator reading stdout,
38
+ * the reporter is for off-box triage.
39
+ */
40
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
41
+ /**
42
+ * A structured logger.
43
+ *
44
+ * MUST NOT throw — logging sits on every path including the error path, where a
45
+ * failing logger would mask the original fault. Implementations swallow their
46
+ * own failures.
47
+ */
48
+ export interface ILogger {
49
+ debug(message: string, fields?: LogFields): void;
50
+ info(message: string, fields?: LogFields): void;
51
+ warn(message: string, fields?: LogFields): void;
52
+ error(message: string, fields?: LogFields): void;
53
+ /**
54
+ * Derive a logger that stamps `fields` onto every record it writes.
55
+ *
56
+ * This is the whole point of the seam: the request hook binds `requestId`
57
+ * (plus method/route) ONCE, and every downstream call site logs without
58
+ * knowing a request id exists. Correlation stops being each call site's
59
+ * problem — which is exactly why the 199 `console.*` calls had none.
60
+ *
61
+ * Children compose; a child of a child carries both parents' fields. A
62
+ * child's field overwrites a parent's on key collision.
63
+ */
64
+ child(fields: LogFields): ILogger;
65
+ }
66
+ /**
67
+ * Default `ILogger` — discards everything. Lets library code log
68
+ * unconditionally without a configured backend, and keeps test output clean.
69
+ *
70
+ * Note this is the DEFAULT, not the fallback used in production: the app wires
71
+ * a real logger at its composition root. A library consumer that wires nothing
72
+ * gets silence rather than unsolicited stdout writes — the same bargain
73
+ * `NoopErrorReporter` makes.
74
+ */
75
+ export declare class NoopLogger implements ILogger {
76
+ debug(_message: string, _fields?: LogFields): void;
77
+ info(_message: string, _fields?: LogFields): void;
78
+ warn(_message: string, _fields?: LogFields): void;
79
+ error(_message: string, _fields?: LogFields): void;
80
+ child(_fields: LogFields): ILogger;
81
+ }
82
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/logging/interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACvB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAChD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACjD;;;;;;;;;;OAUG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,qBAAa,UAAW,YAAW,OAAO;IACzC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,GAAG,IAAI;IAClD,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,GAAG,IAAI;IACjD,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,GAAG,IAAI;IACjD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,GAAG,IAAI;IAClD,KAAK,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO;CAGlC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Structured logging seam. Mirrors the `IErrorReporter` split next door: the
3
+ * interface + a safe default live here, the real backend (pino) ships from
4
+ * `@selvajs/server` so nothing in `@selvajs/platform` gains a logging
5
+ * dependency and browser-side consumers never pull a Node logger into a bundle.
6
+ *
7
+ * Why an interface rather than importing pino directly at call sites:
8
+ * - `@selvajs/platform` and `@selvajs/server` are consumed as libraries. A
9
+ * hard pino import would impose a logger (and its transport config) on every
10
+ * embedder, including ones that already own their logging stack.
11
+ * - Tests capture log output by passing a fake, with no transport to stub.
12
+ * - Call sites stay honest: they describe WHAT happened and hand over fields;
13
+ * formatting, redaction and routing are the backend's business.
14
+ */
15
+ /**
16
+ * Default `ILogger` — discards everything. Lets library code log
17
+ * unconditionally without a configured backend, and keeps test output clean.
18
+ *
19
+ * Note this is the DEFAULT, not the fallback used in production: the app wires
20
+ * a real logger at its composition root. A library consumer that wires nothing
21
+ * gets silence rather than unsolicited stdout writes — the same bargain
22
+ * `NoopErrorReporter` makes.
23
+ */
24
+ export class NoopLogger {
25
+ debug(_message, _fields) { }
26
+ info(_message, _fields) { }
27
+ warn(_message, _fields) { }
28
+ error(_message, _fields) { }
29
+ child(_fields) {
30
+ return this;
31
+ }
32
+ }
33
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/logging/interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAwDH;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAU;IACtB,KAAK,CAAC,QAAgB,EAAE,OAAmB,IAAS,CAAC;IACrD,IAAI,CAAC,QAAgB,EAAE,OAAmB,IAAS,CAAC;IACpD,IAAI,CAAC,QAAgB,EAAE,OAAmB,IAAS,CAAC;IACpD,KAAK,CAAC,QAAgB,EAAE,OAAmB,IAAS,CAAC;IACrD,KAAK,CAAC,OAAkB;QACvB,OAAO,IAAI,CAAC;IACb,CAAC;CACD"}
@@ -18,6 +18,12 @@ export type SolveFailureKind =
18
18
  | 'share_cap'
19
19
  /** Result exceeded the response size limit (HTTP 413). */
20
20
  | 'too_large'
21
+ /**
22
+ * Shed by scheduler backpressure — the per-server queue was full, or the solve
23
+ * sat queued past its wait deadline, so it was rejected before executing
24
+ * (HTTP 503 + Retry-After). Distinct from `compute_error`: no compute ran.
25
+ */
26
+ | 'shed'
21
27
  /** Compute server unreachable or the solve threw for any other reason. */
22
28
  | 'compute_error';
23
29
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/metrics/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB;AAC3B,gEAAgE;AAC9D,IAAI;AACN,8CAA8C;GAC5C,SAAS;AACX,4EAA4E;GAC1E,cAAc;AAChB,sEAAsE;GACpE,cAAc;AAChB,8DAA8D;GAC5D,WAAW;AACb,0DAA0D;GACxD,WAAW;AACb,0EAA0E;GACxE,eAAe,CAAC;AAEnB;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qDAAqD;IACrD,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,EAAE,EAAE,OAAO,CAAC;IACZ,iEAAiE;IACjE,WAAW,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IACrD,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CACvE"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/metrics/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB;AAC3B,gEAAgE;AAC9D,IAAI;AACN,8CAA8C;GAC5C,SAAS;AACX,4EAA4E;GAC1E,cAAc;AAChB,sEAAsE;GACpE,cAAc;AAChB,8DAA8D;GAC5D,WAAW;AACb,0DAA0D;GACxD,WAAW;AACb;;;;GAIG;GACD,MAAM;AACR,0EAA0E;GACxE,eAAe,CAAC;AAEnB;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B,wEAAwE;IACxE,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qDAAqD;IACrD,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,EAAE,EAAE,OAAO,CAAC;IACZ,iEAAiE;IACjE,WAAW,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAChC,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IACrD,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CACvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/metrics/interface.ts"],"names":[],"mappings":"AA8EA;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IAC/B,KAAK,CAAC,MAAM,CAAC,IAAoB,EAAE,OAAoB,IAAkB,CAAC;CAC1E"}
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/metrics/interface.ts"],"names":[],"mappings":"AAoFA;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IAC/B,KAAK,CAAC,MAAM,CAAC,IAAoB,EAAE,OAAoB,IAAkB,CAAC;CAC1E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=slug-schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-schema.test.d.ts","sourceRoot":"","sources":["../../../src/organizations/__tests__/slug-schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SlugSchema, RESERVED_SLUGS } from '../schemas.js';
3
+ /**
4
+ * Reserved-slug guard (audit D4). A tenant slug must never equal a top-level
5
+ * route segment, or it could shadow (or be shadowed by) a route once flat
6
+ * tenant URLs — `/o/{slug}/…` per ADR 0006 — exist. These tests pin that the
7
+ * guard rejects every reserved word and still accepts ordinary slugs, so the
8
+ * reservation can't silently regress before the multi-org routing is built.
9
+ */
10
+ describe('SlugSchema', () => {
11
+ it('accepts a normal lowercase-hyphenated slug', () => {
12
+ expect(SlugSchema.safeParse('acme-studio').success).toBe(true);
13
+ });
14
+ it('rejects every reserved slug', () => {
15
+ for (const reserved of RESERVED_SLUGS) {
16
+ const result = SlugSchema.safeParse(reserved);
17
+ expect(result.success, `"${reserved}" must be rejected`).toBe(false);
18
+ }
19
+ });
20
+ it('reserves the multi-org namespace prefix `o` even though the length gate already blocks it', () => {
21
+ // `o` is 1 char, so `min(3)` rejects it regardless — but it stays in the
22
+ // reserved set so intent survives any future relaxation of the length rule.
23
+ expect(RESERVED_SLUGS).toContain('o');
24
+ expect(SlugSchema.safeParse('o').success).toBe(false);
25
+ });
26
+ it('still enforces the format rules (length, charset)', () => {
27
+ expect(SlugSchema.safeParse('ab').success).toBe(false); // too short
28
+ expect(SlugSchema.safeParse('-lead').success).toBe(false); // leading hyphen
29
+ expect(SlugSchema.safeParse('UPPER').success).toBe(false); // uppercase
30
+ expect(SlugSchema.safeParse('a'.repeat(64)).success).toBe(false); // too long
31
+ });
32
+ it('does not over-reject: a slug that merely contains a reserved word is fine', () => {
33
+ // The guard is exact-match, not substring — `team-alpha` is not `team`.
34
+ expect(SlugSchema.safeParse('team-alpha').success).toBe(true);
35
+ expect(SlugSchema.safeParse('my-api').success).toBe(true);
36
+ });
37
+ });
38
+ //# sourceMappingURL=slug-schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-schema.test.js","sourceRoot":"","sources":["../../../src/organizations/__tests__/slug-schema.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE3D;;;;;;GAMG;AACH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACtC,KAAK,MAAM,QAAQ,IAAI,cAAc,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,QAAQ,oBAAoB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtE,CAAC;IACF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2FAA2F,EAAE,GAAG,EAAE;QACpG,yEAAyE;QACzE,4EAA4E;QAC5E,MAAM,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY;QACpE,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,iBAAiB;QAC5E,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY;QACvE,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACpF,wEAAwE;QACxE,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -2,5 +2,5 @@ export type { Organization, OrgMember, OrgAssets } from './types.js';
2
2
  export type { IOrgStore } from './interface.js';
3
3
  export { orgPaths } from './paths.js';
4
4
  export type { OrgRole, OrgPermission, OrgAssetKind, CreateOrgInput, UpdateOrgInput } from './schemas.js';
5
- export { OrgRoleSchema, OrgPermissionSchema, OrgAssetKindSchema, ALL_ORG_ASSET_KINDS, SlugSchema, CreateOrgSchema, UpdateOrgSchema, ALL_ORG_PERMISSIONS, DEFAULT_ORG_PERMISSIONS, OWNER_ADMIN_ONLY_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from './schemas.js';
5
+ export { OrgRoleSchema, OrgPermissionSchema, OrgAssetKindSchema, ALL_ORG_ASSET_KINDS, SlugSchema, RESERVED_SLUGS, CreateOrgSchema, UpdateOrgSchema, ALL_ORG_PERMISSIONS, DEFAULT_ORG_PERMISSIONS, OWNER_ADMIN_ONLY_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from './schemas.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/organizations/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/organizations/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACX,OAAO,EACP,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,MAAM,cAAc,CAAC"}
@@ -1,3 +1,3 @@
1
1
  export { orgPaths } from './paths.js';
2
- export { OrgRoleSchema, OrgPermissionSchema, OrgAssetKindSchema, ALL_ORG_ASSET_KINDS, SlugSchema, CreateOrgSchema, UpdateOrgSchema, ALL_ORG_PERMISSIONS, DEFAULT_ORG_PERMISSIONS, OWNER_ADMIN_ONLY_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from './schemas.js';
2
+ export { OrgRoleSchema, OrgPermissionSchema, OrgAssetKindSchema, ALL_ORG_ASSET_KINDS, SlugSchema, RESERVED_SLUGS, CreateOrgSchema, UpdateOrgSchema, ALL_ORG_PERMISSIONS, DEFAULT_ORG_PERMISSIONS, OWNER_ADMIN_ONLY_PERMISSIONS, MEMBER_ASSIGNABLE_PERMISSIONS } from './schemas.js';
3
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/organizations/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQtC,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/organizations/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQtC,OAAO,EACN,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,MAAM,cAAc,CAAC"}
@@ -1,9 +1,23 @@
1
1
  import { z } from 'zod';
2
- /** Lowercase alphanumeric with internal hyphens, 3–63 chars. */
2
+ /**
3
+ * Top-level path segments a tenant slug must never equal, so a slug can never
4
+ * shadow (or be shadowed by) a route (audit D4). Two forces meet here:
5
+ * - Reserved multi-org namespace: `/o/{slug}/` is the decided shape for
6
+ * per-org URLs (see ADR 0006). `o` is reserved even though the length gate
7
+ * below already makes a 1-char slug impossible — belt-and-suspenders, and it
8
+ * documents intent for anyone relaxing the length rule later.
9
+ * - Existing flat top-level routes: reserving these keeps a future move to
10
+ * `/o/{slug}/…` (or any flat tenant route) collision-free from day one,
11
+ * before any external link is minted against a slug.
12
+ * Lowercase; the slug is lowercased/matched case-insensitively so `Admin` can't
13
+ * slip through. Keep in sync with the route tree's top-level directories.
14
+ */
15
+ export declare const RESERVED_SLUGS: readonly string[];
16
+ /** Lowercase alphanumeric with internal hyphens, 3–63 chars, not a reserved word. */
3
17
  export declare const SlugSchema: z.ZodString;
4
18
  export declare const OrgRoleSchema: z.ZodEnum<{
5
- owner: "owner";
6
19
  admin: "admin";
20
+ owner: "owner";
7
21
  member: "member";
8
22
  }>;
9
23
  export type OrgRole = z.infer<typeof OrgRoleSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/organizations/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gEAAgE;AAChE,eAAO,MAAM,UAAU,aAOrB,CAAC;AAEH,eAAO,MAAM,aAAa;;;;EAAuC,CAAC;AAClE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;EAA8B,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,mBAAmB,EAAE,SAAS,YAAY,EAA+B,CAAC;AAEvF,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB;;;;;EAK9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB,EAAE,SAAS,aAAa,EAAgC,CAAC;AAEzF,sDAAsD;AACtD,eAAO,MAAM,4BAA4B,EAAE,SAAS,aAAa,EAGhE,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,SAAS,aAAa,EAEjE,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAI7E,CAAC;AAEF,eAAO,MAAM,eAAe;;;iBAG1B,CAAC;AAEH,eAAO,MAAM,eAAe;;;iBAA4B,CAAC;AAEzD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC7D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/organizations/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAY3C,CAAC;AAIF,qFAAqF;AACrF,eAAO,MAAM,UAAU,aAUpB,CAAC;AAEJ,eAAO,MAAM,aAAa;;;;EAAuC,CAAC;AAClE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;EAA8B,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,mBAAmB,EAAE,SAAS,YAAY,EAA+B,CAAC;AAEvF,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB;;;;;EAK9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,mBAAmB,EAAE,SAAS,aAAa,EAAgC,CAAC;AAEzF,sDAAsD;AACtD,eAAO,MAAM,4BAA4B,EAAE,SAAS,aAAa,EAGhE,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,SAAS,aAAa,EAEjE,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAI7E,CAAC;AAEF,eAAO,MAAM,eAAe;;;iBAG1B,CAAC;AAEH,eAAO,MAAM,eAAe;;;iBAA4B,CAAC;AAEzD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC7D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
@@ -1,10 +1,40 @@
1
1
  import { z } from 'zod';
2
- /** Lowercase alphanumeric with internal hyphens, 3–63 chars. */
2
+ /**
3
+ * Top-level path segments a tenant slug must never equal, so a slug can never
4
+ * shadow (or be shadowed by) a route (audit D4). Two forces meet here:
5
+ * - Reserved multi-org namespace: `/o/{slug}/` is the decided shape for
6
+ * per-org URLs (see ADR 0006). `o` is reserved even though the length gate
7
+ * below already makes a 1-char slug impossible — belt-and-suspenders, and it
8
+ * documents intent for anyone relaxing the length rule later.
9
+ * - Existing flat top-level routes: reserving these keeps a future move to
10
+ * `/o/{slug}/…` (or any flat tenant route) collision-free from day one,
11
+ * before any external link is minted against a slug.
12
+ * Lowercase; the slug is lowercased/matched case-insensitively so `Admin` can't
13
+ * slip through. Keep in sync with the route tree's top-level directories.
14
+ */
15
+ export const RESERVED_SLUGS = [
16
+ 'o',
17
+ 'api',
18
+ 'admin',
19
+ 'auth',
20
+ 'login',
21
+ 'logout',
22
+ 'setup',
23
+ 'team',
24
+ 'library',
25
+ 'projects',
26
+ 'accept-invite'
27
+ ];
28
+ const RESERVED_SLUG_SET = new Set(RESERVED_SLUGS);
29
+ /** Lowercase alphanumeric with internal hyphens, 3–63 chars, not a reserved word. */
3
30
  export const SlugSchema = z
4
31
  .string()
5
32
  .min(3, 'Slug must be at least 3 characters')
6
33
  .max(63, 'Slug must be at most 63 characters')
7
- .regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, 'Slug must be lowercase alphanumeric with hyphens (no leading/trailing/consecutive hyphens)');
34
+ .regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, 'Slug must be lowercase alphanumeric with hyphens (no leading/trailing/consecutive hyphens)')
35
+ .refine((s) => !RESERVED_SLUG_SET.has(s), {
36
+ message: 'Slug is reserved and cannot be used'
37
+ });
8
38
  export const OrgRoleSchema = z.enum(['owner', 'admin', 'member']);
9
39
  /**
10
40
  * Kinds of org-scoped branding asset. Adding a new kind is a single entry here
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/organizations/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gEAAgE;AAChE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC;KACzB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;KAC5C,GAAG,CAAC,EAAE,EAAE,oCAAoC,CAAC;KAC7C,KAAK,CACL,iCAAiC,EACjC,4FAA4F,CAC5F,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAGlE;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAG9D,MAAM,CAAC,MAAM,mBAAmB,GAA4B,kBAAkB,CAAC,OAAO,CAAC;AAEvF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,oBAAoB;IACpB,oBAAoB;IACpB,oBAAoB;IACpB,iBAAiB;CACjB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mBAAmB,GAA6B,mBAAmB,CAAC,OAAO,CAAC;AAEzF,sDAAsD;AACtD,MAAM,CAAC,MAAM,4BAA4B,GAA6B;IACrE,oBAAoB;IACpB,oBAAoB;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAA6B,mBAAmB,CAAC,MAAM,CAChG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAChD,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,uBAAuB,GAA8C;IACjF,KAAK,EAAE,CAAC,GAAG,mBAAmB,CAAC;IAC/B,KAAK,EAAE,CAAC,GAAG,mBAAmB,CAAC;IAC/B,MAAM,EAAE,EAAE;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACjE,IAAI,EAAE,UAAU;CAChB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/organizations/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB;IAChD,GAAG;IACH,KAAK;IACL,OAAO;IACP,MAAM;IACN,OAAO;IACP,QAAQ;IACR,OAAO;IACP,MAAM;IACN,SAAS;IACT,UAAU;IACV,eAAe;CACf,CAAC;AAEF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;AAElD,qFAAqF;AACrF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC;KACzB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;KAC5C,GAAG,CAAC,EAAE,EAAE,oCAAoC,CAAC;KAC7C,KAAK,CACL,iCAAiC,EACjC,4FAA4F,CAC5F;KACA,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;IACzC,OAAO,EAAE,qCAAqC;CAC9C,CAAC,CAAC;AAEJ,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAGlE;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAG9D,MAAM,CAAC,MAAM,mBAAmB,GAA4B,kBAAkB,CAAC,OAAO,CAAC;AAEvF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,oBAAoB;IACpB,oBAAoB;IACpB,oBAAoB;IACpB,iBAAiB;CACjB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,mBAAmB,GAA6B,mBAAmB,CAAC,OAAO,CAAC;AAEzF,sDAAsD;AACtD,MAAM,CAAC,MAAM,4BAA4B,GAA6B;IACrE,oBAAoB;IACpB,oBAAoB;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAA6B,mBAAmB,CAAC,MAAM,CAChG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAChD,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,uBAAuB,GAA8C;IACjF,KAAK,EAAE,CAAC,GAAG,mBAAmB,CAAC;IAC/B,KAAK,EAAE,CAAC,GAAG,mBAAmB,CAAC;IAC/B,MAAM,EAAE,EAAE;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IACjE,IAAI,EAAE,UAAU;CAChB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC"}
@@ -0,0 +1,86 @@
1
+ import type { RequestContext } from '../context.js';
2
+ /**
3
+ * Durable L2 solve-result cache (H1). The scheduler's in-process `Map` is L1 —
4
+ * 20 entries, 5-min TTL, per app instance. This is the shared, longer-lived
5
+ * layer keyed on `(orgId, definitionId, versionId, inputKey)` where `inputKey`
6
+ * is a wide (SHA-256) hash of the transformed input tree plus the solve-affecting
7
+ * config subset (H2/R8). A hit skips compute entirely.
8
+ *
9
+ * Design contract (decided 2026-07-11):
10
+ *
11
+ * - **Async from day one.** The memory backend returns promises too, so no call
12
+ * site changes shape when a network store (Redis) arrives later.
13
+ * - **Entries are opaque bytes** — the pre-gzipped response envelope (R6) plus a
14
+ * small metadata header. The backend never inspects them; memory stores the
15
+ * same buffer Redis would, so a hit stays near-CPU-free in either backend and
16
+ * there is no serialization drift between impls.
17
+ * - **Best-effort.** `get` may miss at any time (restart, eviction, network
18
+ * blip); `set` may silently drop. Correctness NEVER depends on cache presence —
19
+ * this is what makes backends interchangeable and what makes the
20
+ * no-TTL/versionId keying safe under any eviction policy.
21
+ * - **Per-definition quota, passed at write time.** `set` carries
22
+ * `maxEntriesForDefinition` so the backend holds no policy, only data. Eviction
23
+ * is LRU *within* the definition: a slider-heavy definition churns only its own
24
+ * entries, not everyone else's.
25
+ * - **Version keying = no invalidation.** Publishing mints a new `versionId` →
26
+ * fresh keyspace; rollback re-hits old entries; there is nothing to invalidate.
27
+ */
28
+ /** The parts that address a cached solve. All four are folded into the stored key. */
29
+ export interface SolveCacheKey {
30
+ /**
31
+ * Owning org (defense-in-depth against cross-tenant reads). Null for
32
+ * remote-URL solves that have no org — those are not cached (the app passes
33
+ * the cache hook only for org-scoped live-channel solves).
34
+ */
35
+ orgId: string;
36
+ /** The definition guid — the quota scope. */
37
+ definitionId: string;
38
+ /** The immutable version id — no TTL needed, so entries never go stale. */
39
+ versionId: string;
40
+ /**
41
+ * Wide hash of the transformed input tree + solve-affecting config subset
42
+ * (SHA-256 hex; see the pipeline's key builder). This is the only part that
43
+ * varies per solve of a given version.
44
+ */
45
+ inputKey: string;
46
+ }
47
+ /** Options for a write; carries the per-definition quota so the backend stays stateless. */
48
+ export interface SolveCacheSetOptions {
49
+ /**
50
+ * Max cached entries this definition may retain. The backend evicts
51
+ * LRU-within-definition down to this count on write. A definition's quota is
52
+ * `solveCacheLimit` (absent → the global default); the app never calls `set`
53
+ * when the resolved quota is `0` (caching off), so this is always `>= 1`.
54
+ */
55
+ maxEntriesForDefinition: number;
56
+ }
57
+ /**
58
+ * Durable solve-result cache. The solve pipeline talks only to this interface;
59
+ * the backend (memory today, Redis later) is a config change, not a redesign.
60
+ *
61
+ * Implementations MUST NOT throw — both methods are best-effort on the hot path
62
+ * of every live solve. A backend failure resolves as a miss (`get`) or a silent
63
+ * drop (`set`); it never becomes a request error.
64
+ */
65
+ export interface ISolveResultCache {
66
+ /**
67
+ * Look up a cached envelope. Resolves the opaque bytes on a hit, or `null` on
68
+ * a miss (including any backend error). Bumps the entry's recency.
69
+ */
70
+ get(ctx: RequestContext, key: SolveCacheKey): Promise<Uint8Array | null>;
71
+ /**
72
+ * Store an envelope, evicting LRU-within-definition down to
73
+ * `opts.maxEntriesForDefinition`. Best-effort: silently drops on any failure.
74
+ */
75
+ set(ctx: RequestContext, key: SolveCacheKey, bytes: Uint8Array, opts: SolveCacheSetOptions): Promise<void>;
76
+ }
77
+ /**
78
+ * Default `ISolveResultCache` — never stores, always misses. Used when
79
+ * `SOLVE_CACHE_PROVIDER=off` (or unset). Swap in the memory backend
80
+ * (`@selvajs/server`) or a shared store to enable the durable L2.
81
+ */
82
+ export declare class NoopSolveResultCache implements ISolveResultCache {
83
+ get(_ctx: RequestContext, _key: SolveCacheKey): Promise<Uint8Array | null>;
84
+ set(_ctx: RequestContext, _key: SolveCacheKey, _bytes: Uint8Array, _opts: SolveCacheSetOptions): Promise<void>;
85
+ }
86
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/solveCache/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,sFAAsF;AACtF,MAAM,WAAW,aAAa;IAC7B;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,4FAA4F;AAC5F,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,uBAAuB,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACzE;;;OAGG;IACH,GAAG,CACF,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,oBAAoB,GACxB,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IACvD,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAG1E,GAAG,CACR,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,oBAAoB,GACzB,OAAO,CAAC,IAAI,CAAC;CAChB"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default `ISolveResultCache` — never stores, always misses. Used when
3
+ * `SOLVE_CACHE_PROVIDER=off` (or unset). Swap in the memory backend
4
+ * (`@selvajs/server`) or a shared store to enable the durable L2.
5
+ */
6
+ export class NoopSolveResultCache {
7
+ async get(_ctx, _key) {
8
+ return null;
9
+ }
10
+ async set(_ctx, _key, _bytes, _opts) { }
11
+ }
12
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/solveCache/interface.ts"],"names":[],"mappings":"AAsFA;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IAChC,KAAK,CAAC,GAAG,CAAC,IAAoB,EAAE,IAAmB;QAClD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,KAAK,CAAC,GAAG,CACR,IAAoB,EACpB,IAAmB,EACnB,MAAkB,EAClB,KAA2B,IACV,CAAC;CACnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"definitionStoreSuite.d.ts","sourceRoot":"","sources":["../../../src/testing/suites/definitionStoreSuite.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAKvE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yFAAyF;IACzF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,iCAAiC;IACjD,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC;IAChE;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,CAAC;IACvE;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AA0ED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,iCAAiC,GAAG,IAAI,CAmf3F"}
1
+ {"version":3,"file":"definitionStoreSuite.d.ts","sourceRoot":"","sources":["../../../src/testing/suites/definitionStoreSuite.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAKvE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yFAAyF;IACzF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,iCAAiC;IACjD,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAC;IAChE;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,CAAC;IACvE;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AA2ED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,iCAAiC,GAAG,IAAI,CAwjB3F"}
@@ -38,6 +38,7 @@ function record(scope, overrides = {}) {
38
38
  displayName: overrides.displayName ?? 'Test',
39
39
  status: overrides.status ?? 'published',
40
40
  solveCount: overrides.solveCount ?? 0,
41
+ nextVersionNumber: overrides.nextVersionNumber ?? 2,
41
42
  liveVersionId: overrides.liveVersionId ?? null,
42
43
  draftVersionId: overrides.draftVersionId ?? null,
43
44
  createdAt: overrides.createdAt ?? now,
@@ -139,6 +140,23 @@ export function runDefinitionStoreConformance(opts) {
139
140
  expect(got?.displayName).toBe('New');
140
141
  expect(got.updatedAt >= created.updatedAt).toBe(true);
141
142
  });
143
+ it('solveCacheLimit round-trips: absent → inherit, set → value, null → cleared', async () => {
144
+ const store = await createStore();
145
+ const scope = await scopeFor();
146
+ const guid = makeUuid();
147
+ await store.create(ctx(scope.ownerId), record(scope, { guid }));
148
+ // Absent on create → undefined (inherit the global default).
149
+ expect((await store.get(ctx(scope.ownerId), guid))?.solveCacheLimit).toBeUndefined();
150
+ // Set a per-definition quota.
151
+ await store.update(ctx(scope.ownerId), guid, { solveCacheLimit: 25 });
152
+ expect((await store.get(ctx(scope.ownerId), guid))?.solveCacheLimit).toBe(25);
153
+ // 0 means "caching off" and must persist as 0 (distinct from absent/inherit).
154
+ await store.update(ctx(scope.ownerId), guid, { solveCacheLimit: 0 });
155
+ expect((await store.get(ctx(scope.ownerId), guid))?.solveCacheLimit).toBe(0);
156
+ // null clears back to inherit.
157
+ await store.update(ctx(scope.ownerId), guid, { solveCacheLimit: null });
158
+ expect((await store.get(ctx(scope.ownerId), guid))?.solveCacheLimit).toBeUndefined();
159
+ });
142
160
  it('update on missing guid throws ProviderError with status 404', async () => {
143
161
  const store = await createStore();
144
162
  const scope = await scopeFor();
@@ -185,6 +203,45 @@ export function runDefinitionStoreConformance(opts) {
185
203
  }
186
204
  expect(threw).toBe(false);
187
205
  });
206
+ it('reserveNextVersionNumber returns the record counter then advances it', async () => {
207
+ const store = await createStore();
208
+ const scope = await scopeFor();
209
+ const guid = makeUuid();
210
+ await store.create(ctx(scope.ownerId), record(scope, { guid, nextVersionNumber: 2 }));
211
+ const first = await store.reserveNextVersionNumber(ctx(scope.ownerId), guid);
212
+ const second = await store.reserveNextVersionNumber(ctx(scope.ownerId), guid);
213
+ expect(first).toBe(2);
214
+ expect(second).toBe(3);
215
+ // Counter persisted, never reused.
216
+ const got = await store.get(ctx(scope.ownerId), guid);
217
+ expect(got?.nextVersionNumber).toBe(4);
218
+ });
219
+ it('reserveNextVersionNumber mints a FRESH number after delete-latest (fileKeys never collide)', async () => {
220
+ // The whole reason the counter exists: max(existing)+1 reused a number
221
+ // after deleting the latest version, colliding the fileKey. The monotonic
222
+ // counter must hand out a number BEYOND any deleted version's.
223
+ const store = await createStore();
224
+ const scope = await scopeFor();
225
+ const guid = makeUuid();
226
+ await store.create(ctx(scope.ownerId), record(scope, { guid, nextVersionNumber: 2 }));
227
+ // Simulate an upload of v2 (reserve, create), then delete it.
228
+ const n2 = await store.reserveNextVersionNumber(ctx(scope.ownerId), guid);
229
+ expect(n2).toBe(2);
230
+ const v2 = version(guid, n2, scope.ownerId);
231
+ await store.createVersion(ctx(scope.ownerId), v2);
232
+ await store.deleteVersion(ctx(scope.ownerId), v2.id);
233
+ // The next reservation must be 3 — NOT 2 again — even though the highest
234
+ // surviving version number is now 1 (v1 was never created here; the point
235
+ // is the counter ignores the version list entirely).
236
+ const n3 = await store.reserveNextVersionNumber(ctx(scope.ownerId), guid);
237
+ expect(n3).toBe(3);
238
+ expect(n3).not.toBe(n2);
239
+ });
240
+ it('reserveNextVersionNumber throws for a missing definition', async () => {
241
+ const store = await createStore();
242
+ const scope = await scopeFor();
243
+ await expect(store.reserveNextVersionNumber(ctx(scope.ownerId), makeUuid())).rejects.toThrow();
244
+ });
188
245
  it('update advances updatedBy to the caller', async () => {
189
246
  const store = await createStore();
190
247
  const scope = await scopeFor();