@selvajs/platform 0.14.2 → 0.15.0-beta.1
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/auth.d.ts +60 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +12 -0
- package/dist/auth.js.map +1 -0
- package/dist/compute.d.ts +47 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +11 -0
- package/dist/compute.js.map +1 -0
- package/dist/computeServer/index.d.ts +2 -0
- package/dist/computeServer/index.d.ts.map +1 -1
- package/dist/computeServer/index.js +6 -0
- package/dist/computeServer/index.js.map +1 -1
- package/dist/computeServer/interface.d.ts +10 -0
- package/dist/computeServer/interface.d.ts.map +1 -1
- package/dist/computeServer/secrets.d.ts +53 -0
- package/dist/computeServer/secrets.d.ts.map +1 -0
- package/dist/computeServer/secrets.js +67 -0
- package/dist/computeServer/secrets.js.map +1 -0
- package/dist/context.d.ts +7 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +13 -0
- package/dist/context.js.map +1 -1
- package/dist/data/interface.d.ts +33 -0
- package/dist/data/interface.d.ts.map +1 -1
- package/dist/definitions/interface.d.ts +18 -0
- 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 +3 -0
- package/dist/definitions/schemas.js.map +1 -1
- package/dist/definitions/service.d.ts +62 -0
- package/dist/definitions/service.d.ts.map +1 -0
- package/dist/definitions/service.js +149 -0
- package/dist/definitions/service.js.map +1 -0
- package/dist/definitions/types.d.ts +24 -1
- package/dist/definitions/types.d.ts.map +1 -1
- package/dist/definitions.d.ts +146 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/definitions.js +23 -0
- package/dist/definitions.js.map +1 -0
- package/dist/errors/interface.d.ts +44 -0
- package/dist/errors/interface.d.ts.map +1 -0
- package/dist/errors/interface.js +9 -0
- package/dist/errors/interface.js.map +1 -0
- package/dist/events/interface.d.ts +23 -0
- package/dist/events/interface.d.ts.map +1 -1
- package/dist/events/interface.js.map +1 -1
- package/dist/index.d.ts +14 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/invites/schemas.d.ts +24 -0
- package/dist/invites/schemas.d.ts.map +1 -0
- package/dist/invites/schemas.js +17 -0
- package/dist/invites/schemas.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/index.d.ts +4 -3
- package/dist/organizations/index.d.ts.map +1 -1
- package/dist/organizations/index.js +2 -1
- package/dist/organizations/index.js.map +1 -1
- package/dist/organizations/interface.d.ts +1 -1
- package/dist/organizations/interface.d.ts.map +1 -1
- package/dist/organizations/paths.d.ts +22 -0
- package/dist/organizations/paths.d.ts.map +1 -0
- package/dist/organizations/paths.js +43 -0
- package/dist/organizations/paths.js.map +1 -0
- package/dist/organizations/schemas.d.ts +10 -0
- package/dist/organizations/schemas.d.ts.map +1 -1
- package/dist/organizations/schemas.js +6 -0
- package/dist/organizations/schemas.js.map +1 -1
- package/dist/organizations/types.d.ts +10 -1
- package/dist/organizations/types.d.ts.map +1 -1
- package/dist/organizations.d.ts +93 -0
- package/dist/organizations.d.ts.map +1 -0
- package/dist/organizations.js +14 -0
- package/dist/organizations.js.map +1 -0
- 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/storage/assetClasses.d.ts +59 -0
- package/dist/storage/assetClasses.d.ts.map +1 -0
- package/dist/storage/assetClasses.js +86 -0
- package/dist/storage/assetClasses.js.map +1 -0
- package/dist/storage/cacheBust.d.ts +21 -0
- package/dist/storage/cacheBust.d.ts.map +1 -0
- package/dist/storage/cacheBust.js +29 -0
- package/dist/storage/cacheBust.js.map +1 -0
- package/dist/storage/image.d.ts +6 -1
- package/dist/storage/image.d.ts.map +1 -1
- package/dist/storage/image.js +8 -3
- package/dist/storage/image.js.map +1 -1
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/testing/adapterSuite.d.ts +33 -0
- package/dist/testing/adapterSuite.d.ts.map +1 -0
- package/dist/testing/adapterSuite.js +166 -0
- package/dist/testing/adapterSuite.js.map +1 -0
- package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/definitionStoreSuite.js +87 -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/orgStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/orgStoreSuite.js +12 -0
- package/dist/testing/suites/orgStoreSuite.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/runner.d.ts +21 -0
- package/dist/testing/suites/runner.d.ts.map +1 -0
- package/dist/testing/suites/runner.js +7 -0
- package/dist/testing/suites/runner.js.map +1 -0
- package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
- package/dist/testing/suites/storageProviderSuite.js +18 -0
- 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 -17
- package/src/computeServer/interface.ts +0 -62
- 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 -66
- 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 -78
- package/src/definitions/paths.ts +0 -31
- package/src/definitions/schemas.ts +0 -64
- package/src/definitions/types.ts +0 -111
- 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 -219
- 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 -14
- package/src/organizations/interface.ts +0 -68
- package/src/organizations/schemas.ts +0 -52
- package/src/organizations/types.ts +0 -31
- 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/image.ts +0 -77
- package/src/storage/index.ts +0 -9
- 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 -585
- 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 -412
- 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 -175
- 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/src/pagination.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { DefinitionStatus } from './definitions/types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cursor-paginated list options. Adapters MUST provide sensible defaults
|
|
5
|
-
* (limit=50, orderBy=createdAt desc) and clamp limit to a safe upper bound.
|
|
6
|
-
*
|
|
7
|
-
* Cursors are opaque — adapters may use row ids, offsets, or keyset
|
|
8
|
-
* encodings. Callers must not inspect them.
|
|
9
|
-
*/
|
|
10
|
-
export interface ListOptions {
|
|
11
|
-
limit?: number;
|
|
12
|
-
cursor?: string;
|
|
13
|
-
orderBy?: 'createdAt' | 'updatedAt' | 'name';
|
|
14
|
-
/** Default: 'desc'. */
|
|
15
|
-
orderDir?: 'asc' | 'desc';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Definition-specific filtering on top of `ListOptions`. */
|
|
19
|
-
export interface DefinitionListOptions extends Omit<ListOptions, 'orderBy'> {
|
|
20
|
-
orderBy?: 'createdAt' | 'updatedAt' | 'name' | 'solveCount';
|
|
21
|
-
/**
|
|
22
|
-
* Include records with `status='pending'`. Default false — consumers don't
|
|
23
|
-
* see half-written state unless they explicitly opt in.
|
|
24
|
-
*/
|
|
25
|
-
includePending?: boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Include records with `status='archived'`. Default false — archived
|
|
28
|
-
* definitions are hidden from normal views; opt in for archive browsers.
|
|
29
|
-
*/
|
|
30
|
-
includeArchived?: boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Filter to specific editorial statuses. Omit for the default view (all
|
|
33
|
-
* non-pending, non-archived). Pass `['published']` for the runner home.
|
|
34
|
-
* When set, `includePending` / `includeArchived` are ignored.
|
|
35
|
-
*/
|
|
36
|
-
statuses?: DefinitionStatus[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface Page<T> {
|
|
40
|
-
items: T[];
|
|
41
|
-
nextCursor?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const DEFAULT_PAGE_LIMIT = 50;
|
|
45
|
-
export const MAX_PAGE_LIMIT = 200;
|
package/src/permissions/index.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { UserManagementResult } from '../auth/types.js';
|
|
2
|
-
import type { RequestContext } from '../context.js';
|
|
3
|
-
import type { PlatformPermission } from './types.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Per-user platform permissions. Lives at the data layer (not on
|
|
7
|
-
* `IAuthProvider`) so external IdPs don't need to model Selva-specific
|
|
8
|
-
* authorization.
|
|
9
|
-
*
|
|
10
|
-
* **The sole-`instance_admin` invariant is enforced here.** Any operation
|
|
11
|
-
* that would leave zero users holding `instance_admin` MUST return
|
|
12
|
-
* `'last_admin'`. Auth providers consult `countInstanceAdminsExcluding`
|
|
13
|
-
* before any destructive op so the invariant holds across edits, deletes,
|
|
14
|
-
* and disables.
|
|
15
|
-
*
|
|
16
|
-
* Reads are scoped to "self or admin"; writes restricted to `instance_admin`.
|
|
17
|
-
* Pass `SYSTEM_CONTEXT` for trusted server flows (bootstrap, hooks).
|
|
18
|
-
*/
|
|
19
|
-
export interface IPlatformPermissionStore {
|
|
20
|
-
/**
|
|
21
|
-
* Read permissions for a single user. Empty array for users with no
|
|
22
|
-
* admin grants (the common case). Non-admin callers may only read self.
|
|
23
|
-
*/
|
|
24
|
-
getFor(ctx: RequestContext, userId: string): Promise<PlatformPermission[]>;
|
|
25
|
-
|
|
26
|
-
/** Batch-read for the admin user list. Admin-only. */
|
|
27
|
-
getForBatch(
|
|
28
|
-
ctx: RequestContext,
|
|
29
|
-
userIds: readonly string[]
|
|
30
|
-
): Promise<Map<string, PlatformPermission[]>>;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Replace the user's permissions. Idempotent. Returns:
|
|
34
|
-
* - `ok` — succeeded
|
|
35
|
-
* - `not_found` — target user doesn't exist
|
|
36
|
-
* - `last_admin` — refused; would leave zero `instance_admin` users
|
|
37
|
-
*
|
|
38
|
-
* `not_supported` is intentionally absent — every deployment must provide
|
|
39
|
-
* a permission store.
|
|
40
|
-
*/
|
|
41
|
-
set(
|
|
42
|
-
ctx: RequestContext,
|
|
43
|
-
userId: string,
|
|
44
|
-
permissions: readonly PlatformPermission[]
|
|
45
|
-
): Promise<UserManagementResult>;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* True if at least one *enabled* user holds `instance_admin`. Used by
|
|
49
|
-
* first-run detection and the destructive-op invariant check. Adapters
|
|
50
|
-
* MUST exclude disabled users (over-count is safer than under-count).
|
|
51
|
-
*/
|
|
52
|
-
hasInstanceAdmin(ctx: RequestContext): Promise<boolean>;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Count *other* users holding `instance_admin`. `deleteUser` / `disableUser`
|
|
56
|
-
* call this BEFORE the destructive op — `count === 0` ⇒ `'last_admin'`.
|
|
57
|
-
* MUST exclude disabled users.
|
|
58
|
-
*/
|
|
59
|
-
countInstanceAdminsExcluding(ctx: RequestContext, excludeUserId: string): Promise<number>;
|
|
60
|
-
}
|
package/src/permissions/types.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Platform-scope permissions. Reserved for Selva-staff and instance operators;
|
|
5
|
-
* regular users hold an empty array.
|
|
6
|
-
*
|
|
7
|
-
* - `instance_admin` — superuser; implies every other permission.
|
|
8
|
-
* - `manage_compute` — configure the instance-wide Rhino.Compute pool.
|
|
9
|
-
* - `manage_instance_users` — disable/enable any user on the instance.
|
|
10
|
-
* - `manage_updates` — run system updates.
|
|
11
|
-
*/
|
|
12
|
-
export const PlatformPermissionSchema = z.enum([
|
|
13
|
-
'instance_admin',
|
|
14
|
-
'manage_compute',
|
|
15
|
-
'manage_instance_users',
|
|
16
|
-
'manage_updates'
|
|
17
|
-
]);
|
|
18
|
-
export type PlatformPermission = z.infer<typeof PlatformPermissionSchema>;
|
|
19
|
-
|
|
20
|
-
export const ALL_PLATFORM_PERMISSIONS: readonly PlatformPermission[] =
|
|
21
|
-
PlatformPermissionSchema.options;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { RequestContext } from '../context.js';
|
|
2
|
-
import type { PlatformProjectGrant } from './types.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Grants for `platform`-visibility projects. Enables `instance_admin` to
|
|
6
|
-
* grant orgs or individual users view-only or view+solve access without
|
|
7
|
-
* creating project membership rows.
|
|
8
|
-
*
|
|
9
|
-
* All deletes are hard-deletes — grants have no downstream references and
|
|
10
|
-
* revocation is immediate.
|
|
11
|
-
*/
|
|
12
|
-
export interface IPlatformProjectGrantStore {
|
|
13
|
-
listByProject(ctx: RequestContext, projectId: string): Promise<PlatformProjectGrant[]>;
|
|
14
|
-
create(ctx: RequestContext, grant: PlatformProjectGrant): Promise<void>;
|
|
15
|
-
delete(ctx: RequestContext, id: string): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Cascade hook: drop every grant for a project. Called by `deleteProject`.
|
|
18
|
-
* No-op when none exist.
|
|
19
|
-
*/
|
|
20
|
-
deleteByProject(ctx: RequestContext, projectId: string): Promise<void>;
|
|
21
|
-
/**
|
|
22
|
-
* Cascade hook: drop every org grant whose `granteeId === orgId`. Called by
|
|
23
|
-
* `deleteOrg`. User grants are identity-scoped and remain. No-op when none
|
|
24
|
-
* exist.
|
|
25
|
-
*/
|
|
26
|
-
deleteByGranteeOrg(ctx: RequestContext, orgId: string): Promise<void>;
|
|
27
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
export const PlatformProjectGranteeTypeSchema = z.enum(['org', 'user']);
|
|
4
|
-
|
|
5
|
-
export const PlatformProjectGrantSchema = z.object({
|
|
6
|
-
id: z.string(),
|
|
7
|
-
projectId: z.string(),
|
|
8
|
-
granteeType: PlatformProjectGranteeTypeSchema,
|
|
9
|
-
granteeId: z.string(),
|
|
10
|
-
canSolve: z.boolean(),
|
|
11
|
-
createdBy: z.string(),
|
|
12
|
-
createdAt: z.string()
|
|
13
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export type PlatformProjectGranteeType = 'org' | 'user';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Grants view-only or view+solve access to a `platform`-visibility project
|
|
5
|
-
* for an org or an individual user, without creating project membership rows.
|
|
6
|
-
*
|
|
7
|
-
* - `granteeType: 'org'` → applies to all members of that org
|
|
8
|
-
* - `granteeType: 'user'` → applies to that specific user regardless of org
|
|
9
|
-
* - `canSolve: false` → schema fetch / view only; submit-solve is blocked
|
|
10
|
-
* - `canSolve: true` → view + solve
|
|
11
|
-
*
|
|
12
|
-
* Grants are hard-deleted on revocation — no soft-delete.
|
|
13
|
-
*/
|
|
14
|
-
export interface PlatformProjectGrant {
|
|
15
|
-
id: string;
|
|
16
|
-
projectId: string;
|
|
17
|
-
granteeType: PlatformProjectGranteeType;
|
|
18
|
-
granteeId: string;
|
|
19
|
-
canSolve: boolean;
|
|
20
|
-
createdBy: string;
|
|
21
|
-
createdAt: string;
|
|
22
|
-
}
|
package/src/projects/index.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export type { Project, ProjectMember } from './types.js';
|
|
2
|
-
export type { IProjectStore } from './interface.js';
|
|
3
|
-
export type { ProjectVisibility, ProjectRole } from './schemas.js';
|
|
4
|
-
export { ProjectVisibilitySchema, ProjectRoleSchema, validateProjectFlags } from './schemas.js';
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { RequestContext } from '../context.js';
|
|
2
|
-
import type { ListOptions, Page } from '../pagination.js';
|
|
3
|
-
import type { Project, ProjectMember } from './types.js';
|
|
4
|
-
import type { ProjectRole } from './schemas.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Projects + project members. Soft-delete cascades to members and definitions;
|
|
8
|
-
* see also the cross-store cascade hook on `IPlatformProjectGrantStore`.
|
|
9
|
-
*/
|
|
10
|
-
export interface IProjectStore {
|
|
11
|
-
// Projects
|
|
12
|
-
listProjects(ctx: RequestContext, orgId: string, opts?: ListOptions): Promise<Page<Project>>;
|
|
13
|
-
getProject(ctx: RequestContext, id: string): Promise<Project | null>;
|
|
14
|
-
getProjectBySlug(ctx: RequestContext, orgId: string, slug: string): Promise<Project | null>;
|
|
15
|
-
createProject(ctx: RequestContext, project: Project): Promise<void>;
|
|
16
|
-
updateProject(
|
|
17
|
-
ctx: RequestContext,
|
|
18
|
-
id: string,
|
|
19
|
-
patch: Partial<
|
|
20
|
-
Pick<Project, 'name' | 'slug' | 'description' | 'visibility' | 'autoJoinOnUpload'>
|
|
21
|
-
>
|
|
22
|
-
): Promise<void>;
|
|
23
|
-
/**
|
|
24
|
-
* Soft-delete the project. Cascades to project members and definitions.
|
|
25
|
-
* Definition versions and share links cascade through the definition delete.
|
|
26
|
-
*/
|
|
27
|
-
deleteProject(ctx: RequestContext, id: string): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Reactivate a previously soft-deleted project identified by its org + slug.
|
|
30
|
-
* Clears `deleted_at` and reactivates the owner's `project_members` row.
|
|
31
|
-
* Returns the live project, or `null` if no tombstone with that slug exists.
|
|
32
|
-
*
|
|
33
|
-
* Use this when `createProject` fails with a duplicate-key error on a slug
|
|
34
|
-
* that was soft-deleted — the unconditional unique constraint blocks creation
|
|
35
|
-
* even though `getProjectBySlug` returns null for tombstones.
|
|
36
|
-
*/
|
|
37
|
-
reactivateProject(ctx: RequestContext, orgId: string, slug: string): Promise<Project | null>;
|
|
38
|
-
|
|
39
|
-
// Project members
|
|
40
|
-
listProjectMembers(
|
|
41
|
-
ctx: RequestContext,
|
|
42
|
-
projectId: string,
|
|
43
|
-
opts?: ListOptions
|
|
44
|
-
): Promise<Page<ProjectMember>>;
|
|
45
|
-
getProjectMember(
|
|
46
|
-
ctx: RequestContext,
|
|
47
|
-
projectId: string,
|
|
48
|
-
userId: string
|
|
49
|
-
): Promise<ProjectMember | null>;
|
|
50
|
-
addProjectMember(ctx: RequestContext, member: ProjectMember): Promise<void>;
|
|
51
|
-
updateProjectMemberRole(
|
|
52
|
-
ctx: RequestContext,
|
|
53
|
-
projectId: string,
|
|
54
|
-
userId: string,
|
|
55
|
-
role: ProjectRole
|
|
56
|
-
): Promise<void>;
|
|
57
|
-
removeProjectMember(ctx: RequestContext, projectId: string, userId: string): Promise<void>;
|
|
58
|
-
}
|
package/src/projects/schemas.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
export const ProjectVisibilitySchema = z.enum(['public', 'org', 'private', 'platform']);
|
|
4
|
-
export type ProjectVisibility = z.infer<typeof ProjectVisibilitySchema>;
|
|
5
|
-
|
|
6
|
-
export const ProjectRoleSchema = z.enum(['owner', 'editor', 'viewer']);
|
|
7
|
-
export type ProjectRole = z.infer<typeof ProjectRoleSchema>;
|
|
8
|
-
|
|
9
|
-
/** Cross-field check for PATCH flows. Call on the merged post-patch shape. */
|
|
10
|
-
export function validateProjectFlags(merged: {
|
|
11
|
-
visibility: ProjectVisibility;
|
|
12
|
-
autoJoinOnUpload?: boolean;
|
|
13
|
-
}): { path: string; message: string }[] {
|
|
14
|
-
const issues: { path: string; message: string }[] = [];
|
|
15
|
-
if (merged.autoJoinOnUpload && merged.visibility !== 'public') {
|
|
16
|
-
issues.push({
|
|
17
|
-
path: 'autoJoinOnUpload',
|
|
18
|
-
message: 'autoJoinOnUpload requires visibility=public'
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
if (merged.visibility === 'platform' && merged.autoJoinOnUpload) {
|
|
22
|
-
issues.push({
|
|
23
|
-
path: 'autoJoinOnUpload',
|
|
24
|
-
message: 'autoJoinOnUpload is not allowed on platform projects'
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
return issues;
|
|
28
|
-
}
|
package/src/projects/types.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { ProjectRole, ProjectVisibility } from './schemas.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Visibility:
|
|
5
|
-
* - `public` — any authenticated user can view/solve (cross-org gated by `ALLOW_CROSS_ORG_PUBLIC`)
|
|
6
|
-
* - `org` — any member of the parent organization
|
|
7
|
-
* - `private` — only listed `ProjectMember`s
|
|
8
|
-
* - `platform` — instance-admin-managed; access via `IPlatformProjectGrantStore`
|
|
9
|
-
*/
|
|
10
|
-
export interface Project {
|
|
11
|
-
id: string;
|
|
12
|
-
orgId: string;
|
|
13
|
-
name: string;
|
|
14
|
-
slug: string;
|
|
15
|
-
description?: string;
|
|
16
|
-
visibility: ProjectVisibility;
|
|
17
|
-
/** Transferable; distinct from `createdBy`. */
|
|
18
|
-
ownerId: string;
|
|
19
|
-
createdBy: string;
|
|
20
|
-
updatedBy: string;
|
|
21
|
-
/** Commons mode — only valid when `visibility='public'`. */
|
|
22
|
-
autoJoinOnUpload: boolean;
|
|
23
|
-
createdAt: string;
|
|
24
|
-
updatedAt: string;
|
|
25
|
-
deletedAt?: string | null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface ProjectMember {
|
|
29
|
-
projectId: string;
|
|
30
|
-
userId: string;
|
|
31
|
-
role: ProjectRole;
|
|
32
|
-
joinedAt: string;
|
|
33
|
-
updatedAt: string;
|
|
34
|
-
updatedBy: string;
|
|
35
|
-
deletedAt?: string | null;
|
|
36
|
-
}
|
package/src/shareLinks/index.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export type { ShareLink } from './types.js';
|
|
2
|
-
export type { IShareLinkStore } from './interface.js';
|
|
3
|
-
export { DEFAULT_SHARE_LINK_MAX_SOLVES } from './types.js';
|
|
4
|
-
export type { CreateShareLinkInput } from './schemas.js';
|
|
5
|
-
export { CreateShareLinkInputSchema } from './schemas.js';
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { RequestContext } from '../context.js';
|
|
2
|
-
import type { ListOptions, Page } from '../pagination.js';
|
|
3
|
-
import type { ShareLink } from './types.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Per-definition tokens granting unauthenticated access to one
|
|
7
|
-
* (definitionId, channel). The store sees only the HMAC hash; the raw token
|
|
8
|
-
* is generated and shown to the minter at the route layer.
|
|
9
|
-
*
|
|
10
|
-
* Reads filter `revokedAt IS NULL` defensively.
|
|
11
|
-
*/
|
|
12
|
-
export interface IShareLinkStore {
|
|
13
|
-
/** Insert a new link. Caller has already hashed the raw token. */
|
|
14
|
-
create(ctx: RequestContext, link: ShareLink): Promise<void>;
|
|
15
|
-
/** Newest first by `createdAt`. Excludes revoked links. */
|
|
16
|
-
listByDefinition(
|
|
17
|
-
ctx: RequestContext,
|
|
18
|
-
definitionId: string,
|
|
19
|
-
opts?: ListOptions
|
|
20
|
-
): Promise<Page<ShareLink>>;
|
|
21
|
-
getById(ctx: RequestContext, id: string): Promise<ShareLink | null>;
|
|
22
|
-
/**
|
|
23
|
-
* Lookup by HMAC hash. Returns null when the link doesn't exist OR is
|
|
24
|
-
* revoked OR its parent definition is soft-deleted.
|
|
25
|
-
*/
|
|
26
|
-
getByTokenHash(ctx: RequestContext, tokenHash: string): Promise<ShareLink | null>;
|
|
27
|
-
/** Soft-delete (set `revokedAt`). Idempotent. */
|
|
28
|
-
revoke(ctx: RequestContext, id: string): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* Atomic check-and-increment. Returns the new `solveCount`, or null when
|
|
31
|
-
* the cap was reached. MUST be a single statement — read-then-write races
|
|
32
|
-
* under load. `null` maxSolves means uncapped.
|
|
33
|
-
*/
|
|
34
|
-
tryIncrementSolveCount(ctx: RequestContext, id: string): Promise<number | null>;
|
|
35
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { DefinitionChannelSchema } from '../definitions/schemas.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* HTTP body for `POST /api/definitions/[guid]/share-links`. The route returns
|
|
6
|
-
* the raw token exactly once.
|
|
7
|
-
*/
|
|
8
|
-
export const CreateShareLinkInputSchema = z.object({
|
|
9
|
-
channel: DefinitionChannelSchema.default('live'),
|
|
10
|
-
name: z.string().max(128).optional(),
|
|
11
|
-
allowSolve: z.boolean().default(true),
|
|
12
|
-
/**
|
|
13
|
-
* Null explicitly removes the cap. Omitted = apply default — null is the
|
|
14
|
-
* minter's deliberate "uncap" choice.
|
|
15
|
-
*/
|
|
16
|
-
maxSolves: z.number().int().min(1).nullable().optional(),
|
|
17
|
-
/** Optional ISO timestamp. Resolution compares to `now()`. */
|
|
18
|
-
expiresAt: z.iso.datetime().nullable().optional()
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export type CreateShareLinkInput = z.infer<typeof CreateShareLinkInputSchema>;
|
package/src/shareLinks/types.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { DefinitionChannel } from '../definitions/types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Per-definition token granting access to one (definitionId, channel)
|
|
5
|
-
* without an account. The raw token is HMAC-hashed at rest; `tokenHash` is
|
|
6
|
-
* the stored value. The plaintext token is shown to the minter exactly once.
|
|
7
|
-
*/
|
|
8
|
-
export interface ShareLink {
|
|
9
|
-
id: string;
|
|
10
|
-
definitionId: string;
|
|
11
|
-
channel: DefinitionChannel;
|
|
12
|
-
/** HMAC of the raw token. Lookup hashes the supplied token, then matches. */
|
|
13
|
-
tokenHash: string;
|
|
14
|
-
/** Optional UX label, e.g. "Demo for Acme". */
|
|
15
|
-
name?: string;
|
|
16
|
-
createdBy: string;
|
|
17
|
-
createdAt: string;
|
|
18
|
-
/** Null = never expires. */
|
|
19
|
-
expiresAt?: string | null;
|
|
20
|
-
/** Non-null = revoked. Resolution checks IS NULL. */
|
|
21
|
-
revokedAt?: string | null;
|
|
22
|
-
/** false = view/schema only; true = solve allowed. */
|
|
23
|
-
allowSolve: boolean;
|
|
24
|
-
/** Per-link cap. Null = unlimited. */
|
|
25
|
-
maxSolves?: number | null;
|
|
26
|
-
solveCount: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Default cap applied when the minter doesn't specify one. Tokens are
|
|
31
|
-
* leak-prone by design (visible in iframes), so a default bounds the
|
|
32
|
-
* worst-case denial-of-wallet damage.
|
|
33
|
-
*/
|
|
34
|
-
export const DEFAULT_SHARE_LINK_MAX_SOLVES = 1000;
|
package/src/storage/image.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared image-transcoding helper for `IStorageProvider` implementations.
|
|
3
|
-
* Cover images are normalized to WebP at upload time (capped at 1200px,
|
|
4
|
-
* quality 85) so storage/bandwidth stays predictable and the viewer path
|
|
5
|
-
* is uniform.
|
|
6
|
-
*
|
|
7
|
-
* `sharp` is loaded via a dynamic import so the platform package has no
|
|
8
|
-
* hard runtime dependency on it — install it in providers that need
|
|
9
|
-
* transcoding (optional peer dependency).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export const IMAGE_MAX_WIDTH = 1200;
|
|
13
|
-
export const IMAGE_WEBP_QUALITY = 85;
|
|
14
|
-
|
|
15
|
-
export interface TranscodeResult {
|
|
16
|
-
data: Uint8Array;
|
|
17
|
-
contentType: string;
|
|
18
|
-
/** Possibly-rewritten path (non-webp inputs become `.webp`). */
|
|
19
|
-
path: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** True if the input looks like an image to process — by content type or path extension. */
|
|
23
|
-
export function isImageUpload(contentType: string | undefined, storagePath: string): boolean {
|
|
24
|
-
if (contentType?.startsWith('image/')) return true;
|
|
25
|
-
return /\.(webp|png|jpe?g|gif|bmp|tif?f)$/i.test(storagePath);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Rewrite a path's extension to `.webp`. Unchanged if not a recognized image extension. */
|
|
29
|
-
export function toWebpPath(storagePath: string): string {
|
|
30
|
-
return storagePath.replace(/\.(png|jpe?g|gif|bmp|tif?f)$/i, '.webp');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* If the input is an image, transcode to WebP (capped + re-encoded) and
|
|
35
|
-
* return rewritten bytes/content-type/path. Non-images pass through, so
|
|
36
|
-
* providers can call this unconditionally before `put`.
|
|
37
|
-
*
|
|
38
|
-
* Throws if `sharp` isn't installed — that's a config error, not a fallback.
|
|
39
|
-
*/
|
|
40
|
-
export async function transcodeImageIfNeeded(
|
|
41
|
-
data: Uint8Array,
|
|
42
|
-
contentType: string | undefined,
|
|
43
|
-
storagePath: string
|
|
44
|
-
): Promise<TranscodeResult> {
|
|
45
|
-
if (!isImageUpload(contentType, storagePath)) {
|
|
46
|
-
return { data, contentType: contentType ?? 'application/octet-stream', path: storagePath };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const sharp = await loadSharp();
|
|
50
|
-
const compressed = await sharp(Buffer.from(data))
|
|
51
|
-
.resize({ width: IMAGE_MAX_WIDTH, withoutEnlargement: true })
|
|
52
|
-
.webp({ quality: IMAGE_WEBP_QUALITY })
|
|
53
|
-
.toBuffer();
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
data: new Uint8Array(compressed),
|
|
57
|
-
contentType: 'image/webp',
|
|
58
|
-
path: toWebpPath(storagePath)
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
type SharpFn = typeof import('sharp');
|
|
63
|
-
let cachedSharp: SharpFn | null = null;
|
|
64
|
-
|
|
65
|
-
async function loadSharp(): Promise<SharpFn> {
|
|
66
|
-
if (cachedSharp) return cachedSharp;
|
|
67
|
-
try {
|
|
68
|
-
const mod = (await import('sharp')) as unknown as { default: SharpFn };
|
|
69
|
-
cachedSharp = mod.default ?? (mod as unknown as SharpFn);
|
|
70
|
-
return cachedSharp;
|
|
71
|
-
} catch (err) {
|
|
72
|
-
throw new Error(
|
|
73
|
-
'Image transcoding requires `sharp` to be installed. Add it as a dependency in your provider package.',
|
|
74
|
-
{ cause: err as Error }
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
}
|
package/src/storage/index.ts
DELETED
package/src/storage/interface.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic path-based blob storage. Implement with: local filesystem, S3,
|
|
3
|
-
* Supabase Storage, Azure Blob, etc.
|
|
4
|
-
*
|
|
5
|
-
* **Authorization is the caller's responsibility.** Methods do NOT take a
|
|
6
|
-
* `RequestContext` — the path itself names the resource, and constructing it
|
|
7
|
-
* requires already-authorized scope. Routes/services MUST authorize against
|
|
8
|
-
* `@selvajs/platform/access` BEFORE constructing the path.
|
|
9
|
-
*/
|
|
10
|
-
export interface IStorageProvider {
|
|
11
|
-
/** Returns null if the path does not exist. */
|
|
12
|
-
get(path: string): Promise<Uint8Array | null>;
|
|
13
|
-
|
|
14
|
-
put(path: string, data: Uint8Array, contentType?: string): Promise<void>;
|
|
15
|
-
|
|
16
|
-
/** No-op if the path does not exist. */
|
|
17
|
-
delete(path: string): Promise<void>;
|
|
18
|
-
|
|
19
|
-
/** Delete every file whose path starts with `prefix`. */
|
|
20
|
-
deletePrefix(prefix: string): Promise<void>;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Public URL for a stored file. Local: a relative API path; S3/CDN: a
|
|
24
|
-
* full or signed URL.
|
|
25
|
-
*/
|
|
26
|
-
getPublicUrl(path: string): string;
|
|
27
|
-
}
|
package/src/testing/index.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export { runAuthProviderConformance } from './suites/authProviderSuite.js';
|
|
2
|
-
export type { AuthProviderConformanceOptions } from './suites/authProviderSuite.js';
|
|
3
|
-
|
|
4
|
-
export { runEmailLinkAuthConformance } from './suites/emailLinkAuthSuite.js';
|
|
5
|
-
export type { EmailLinkAuthConformanceOptions } from './suites/emailLinkAuthSuite.js';
|
|
6
|
-
|
|
7
|
-
export { runDefinitionStoreConformance } from './suites/definitionStoreSuite.js';
|
|
8
|
-
export type { DefinitionStoreConformanceOptions } from './suites/definitionStoreSuite.js';
|
|
9
|
-
|
|
10
|
-
export { runOrgStoreConformance } from './suites/orgStoreSuite.js';
|
|
11
|
-
export type { OrgStoreConformanceOptions } from './suites/orgStoreSuite.js';
|
|
12
|
-
|
|
13
|
-
export { runProjectStoreConformance } from './suites/projectStoreSuite.js';
|
|
14
|
-
export type { ProjectStoreConformanceOptions } from './suites/projectStoreSuite.js';
|
|
15
|
-
|
|
16
|
-
export { runStorageProviderConformance } from './suites/storageProviderSuite.js';
|
|
17
|
-
export type { StorageProviderConformanceOptions } from './suites/storageProviderSuite.js';
|
|
18
|
-
|
|
19
|
-
export { runUserProfileStoreConformance } from './suites/userProfileStoreSuite.js';
|
|
20
|
-
export type { UserProfileStoreConformanceOptions } from './suites/userProfileStoreSuite.js';
|
|
21
|
-
|
|
22
|
-
export { runInviteStoreConformance } from './suites/inviteStoreSuite.js';
|
|
23
|
-
export type { InviteStoreConformanceOptions, InviteTestScope } from './suites/inviteStoreSuite.js';
|
|
24
|
-
|
|
25
|
-
export { runComputeServerStoreConformance } from './suites/computeServerStoreSuite.js';
|
|
26
|
-
export type { ComputeServerStoreConformanceOptions } from './suites/computeServerStoreSuite.js';
|
|
27
|
-
|
|
28
|
-
export { runShareLinkStoreConformance } from './suites/shareLinkStoreSuite.js';
|
|
29
|
-
export type {
|
|
30
|
-
ShareLinkStoreConformanceOptions,
|
|
31
|
-
ShareLinkTestScope
|
|
32
|
-
} from './suites/shareLinkStoreSuite.js';
|
|
33
|
-
|
|
34
|
-
export { runEventSinkConformance, RecordingEventSink } from './suites/eventSinkSuite.js';
|
|
35
|
-
export type { EventSinkConformanceOptions } from './suites/eventSinkSuite.js';
|
|
36
|
-
|
|
37
|
-
export { runSolveMetricSinkConformance } from './suites/solveMetricSinkSuite.js';
|
|
38
|
-
export type {
|
|
39
|
-
SolveMetricSinkConformanceOptions,
|
|
40
|
-
RecordedSolveMetric
|
|
41
|
-
} from './suites/solveMetricSinkSuite.js';
|
|
42
|
-
|
|
43
|
-
export { runPlatformPermissionStoreConformance } from './suites/platformPermissionStoreSuite.js';
|
|
44
|
-
export type { PlatformPermissionStoreConformanceOptions } from './suites/platformPermissionStoreSuite.js';
|
|
45
|
-
|
|
46
|
-
export { runPlatformProjectGrantStoreConformance } from './suites/platformProjectGrantStoreSuite.js';
|
|
47
|
-
export type { PlatformProjectGrantStoreConformanceOptions } from './suites/platformProjectGrantStoreSuite.js';
|