@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.
- package/LICENSE +1 -1
- package/dist/computeServer/index.d.ts +1 -1
- package/dist/computeServer/index.d.ts.map +1 -1
- package/dist/computeServer/interface.d.ts +29 -1
- package/dist/computeServer/interface.d.ts.map +1 -1
- package/dist/computeServer/types.d.ts +12 -1
- package/dist/computeServer/types.d.ts.map +1 -1
- package/dist/computeServer/types.js.map +1 -1
- package/dist/data/index.d.ts +2 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -1
- package/dist/data/index.js.map +1 -1
- package/dist/data/interface.d.ts +69 -8
- package/dist/data/interface.d.ts.map +1 -1
- package/dist/data/interface.js +7 -1
- package/dist/data/interface.js.map +1 -1
- package/dist/definitions/interface.d.ts +16 -1
- package/dist/definitions/interface.d.ts.map +1 -1
- package/dist/definitions/schemas.d.ts +1 -0
- package/dist/definitions/schemas.d.ts.map +1 -1
- package/dist/definitions/schemas.js +8 -0
- package/dist/definitions/schemas.js.map +1 -1
- package/dist/definitions/types.d.ts +24 -1
- package/dist/definitions/types.d.ts.map +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/events/interface.d.ts +30 -0
- package/dist/events/interface.d.ts.map +1 -1
- package/dist/events/interface.js +7 -0
- package/dist/events/interface.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/logging/interface.d.ts +82 -0
- package/dist/logging/interface.d.ts.map +1 -0
- package/dist/logging/interface.js +33 -0
- package/dist/logging/interface.js.map +1 -0
- package/dist/metrics/interface.d.ts +6 -0
- package/dist/metrics/interface.d.ts.map +1 -1
- package/dist/metrics/interface.js.map +1 -1
- package/dist/organizations/__tests__/slug-schema.test.d.ts +2 -0
- package/dist/organizations/__tests__/slug-schema.test.d.ts.map +1 -0
- package/dist/organizations/__tests__/slug-schema.test.js +38 -0
- package/dist/organizations/__tests__/slug-schema.test.js.map +1 -0
- package/dist/organizations/index.d.ts +1 -1
- package/dist/organizations/index.d.ts.map +1 -1
- package/dist/organizations/index.js +1 -1
- package/dist/organizations/index.js.map +1 -1
- package/dist/organizations/schemas.d.ts +16 -2
- package/dist/organizations/schemas.d.ts.map +1 -1
- package/dist/organizations/schemas.js +32 -2
- package/dist/organizations/schemas.js.map +1 -1
- package/dist/solveCache/interface.d.ts +86 -0
- package/dist/solveCache/interface.d.ts.map +1 -0
- package/dist/solveCache/interface.js +12 -0
- package/dist/solveCache/interface.js.map +1 -0
- package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/definitionStoreSuite.js +57 -0
- package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
- package/dist/testing/suites/eventSinkSuite.js +1 -0
- package/dist/testing/suites/eventSinkSuite.js.map +1 -1
- package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
- package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
- package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
- package/dist/testing/suites/solveMetricSinkSuite.d.ts +5 -0
- package/dist/testing/suites/solveMetricSinkSuite.d.ts.map +1 -1
- package/dist/testing/suites/solveMetricSinkSuite.js +17 -8
- package/dist/testing/suites/solveMetricSinkSuite.js.map +1 -1
- package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
- package/dist/testing/suites/storageProviderSuite.js +23 -2
- package/dist/testing/suites/storageProviderSuite.js.map +1 -1
- package/package.json +9 -24
- package/src/access/index.ts +0 -23
- package/src/access/rules.ts +0 -276
- package/src/auth/index.ts +0 -8
- package/src/auth/interface.ts +0 -279
- package/src/auth/types.ts +0 -32
- package/src/bindings/index.ts +0 -2
- package/src/bindings/interface.ts +0 -61
- package/src/computeServer/index.ts +0 -28
- package/src/computeServer/interface.ts +0 -73
- package/src/computeServer/secrets.ts +0 -102
- package/src/computeServer/types.ts +0 -74
- package/src/computeServer/utils.ts +0 -112
- package/src/config.ts +0 -99
- package/src/context.ts +0 -80
- package/src/data/index.ts +0 -1
- package/src/data/interface.ts +0 -70
- package/src/definitions/index.ts +0 -26
- package/src/definitions/interface.ts +0 -87
- package/src/definitions/paths.ts +0 -31
- package/src/definitions/schemas.ts +0 -64
- package/src/definitions/types.ts +0 -111
- package/src/errors/interface.ts +0 -45
- package/src/errors.ts +0 -16
- package/src/events/audit.ts +0 -67
- package/src/events/index.ts +0 -9
- package/src/events/interface.ts +0 -102
- package/src/index.ts +0 -247
- package/src/invites/index.ts +0 -2
- package/src/invites/interface.ts +0 -38
- package/src/invites/types.ts +0 -39
- package/src/metrics/interface.ts +0 -86
- package/src/organizations/index.ts +0 -23
- package/src/organizations/interface.ts +0 -68
- package/src/organizations/paths.ts +0 -45
- package/src/organizations/schemas.ts +0 -61
- package/src/organizations/types.ts +0 -41
- package/src/pagination.ts +0 -45
- package/src/permissions/index.ts +0 -3
- package/src/permissions/interface.ts +0 -60
- package/src/permissions/types.ts +0 -21
- package/src/platformProjects/index.ts +0 -3
- package/src/platformProjects/interface.ts +0 -27
- package/src/platformProjects/schemas.ts +0 -13
- package/src/platformProjects/types.ts +0 -22
- package/src/projects/index.ts +0 -4
- package/src/projects/interface.ts +0 -58
- package/src/projects/schemas.ts +0 -28
- package/src/projects/types.ts +0 -36
- package/src/shareLinks/index.ts +0 -5
- package/src/shareLinks/interface.ts +0 -35
- package/src/shareLinks/schemas.ts +0 -21
- package/src/shareLinks/types.ts +0 -34
- package/src/storage/assetClasses.ts +0 -134
- package/src/storage/cacheBust.ts +0 -26
- package/src/storage/image.ts +0 -82
- package/src/storage/index.ts +0 -12
- package/src/storage/interface.ts +0 -27
- package/src/testing/index.ts +0 -47
- package/src/testing/suites/authProviderSuite.ts +0 -207
- package/src/testing/suites/computeServerStoreSuite.ts +0 -195
- package/src/testing/suites/definitionStoreSuite.ts +0 -630
- package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
- package/src/testing/suites/eventSinkSuite.ts +0 -379
- package/src/testing/suites/helpers.ts +0 -87
- package/src/testing/suites/inviteStoreSuite.ts +0 -143
- package/src/testing/suites/orgStoreSuite.ts +0 -429
- package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
- package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
- package/src/testing/suites/projectStoreSuite.ts +0 -381
- package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
- package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
- package/src/testing/suites/storageProviderSuite.ts +0 -196
- package/src/testing/suites/userProfileStoreSuite.ts +0 -134
- package/src/userProfile/index.ts +0 -3
- package/src/userProfile/interface.ts +0 -48
- package/src/userProfile/types.ts +0 -24
- package/src/utils/audit.ts +0 -46
- 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;
|
|
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":"
|
|
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 @@
|
|
|
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
|
-
/**
|
|
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,
|
|
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
|
-
/**
|
|
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,
|
|
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;
|
|
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();
|