@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.
Files changed (196) hide show
  1. package/LICENSE +1 -1
  2. package/dist/auth.d.ts +60 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +12 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/compute.d.ts +47 -0
  7. package/dist/compute.d.ts.map +1 -0
  8. package/dist/compute.js +11 -0
  9. package/dist/compute.js.map +1 -0
  10. package/dist/computeServer/index.d.ts +2 -0
  11. package/dist/computeServer/index.d.ts.map +1 -1
  12. package/dist/computeServer/index.js +6 -0
  13. package/dist/computeServer/index.js.map +1 -1
  14. package/dist/computeServer/interface.d.ts +10 -0
  15. package/dist/computeServer/interface.d.ts.map +1 -1
  16. package/dist/computeServer/secrets.d.ts +53 -0
  17. package/dist/computeServer/secrets.d.ts.map +1 -0
  18. package/dist/computeServer/secrets.js +67 -0
  19. package/dist/computeServer/secrets.js.map +1 -0
  20. package/dist/context.d.ts +7 -0
  21. package/dist/context.d.ts.map +1 -1
  22. package/dist/context.js +13 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/data/interface.d.ts +33 -0
  25. package/dist/data/interface.d.ts.map +1 -1
  26. package/dist/definitions/interface.d.ts +18 -0
  27. package/dist/definitions/interface.d.ts.map +1 -1
  28. package/dist/definitions/schemas.d.ts +1 -0
  29. package/dist/definitions/schemas.d.ts.map +1 -1
  30. package/dist/definitions/schemas.js +3 -0
  31. package/dist/definitions/schemas.js.map +1 -1
  32. package/dist/definitions/service.d.ts +62 -0
  33. package/dist/definitions/service.d.ts.map +1 -0
  34. package/dist/definitions/service.js +149 -0
  35. package/dist/definitions/service.js.map +1 -0
  36. package/dist/definitions/types.d.ts +24 -1
  37. package/dist/definitions/types.d.ts.map +1 -1
  38. package/dist/definitions.d.ts +146 -0
  39. package/dist/definitions.d.ts.map +1 -0
  40. package/dist/definitions.js +23 -0
  41. package/dist/definitions.js.map +1 -0
  42. package/dist/errors/interface.d.ts +44 -0
  43. package/dist/errors/interface.d.ts.map +1 -0
  44. package/dist/errors/interface.js +9 -0
  45. package/dist/errors/interface.js.map +1 -0
  46. package/dist/events/interface.d.ts +23 -0
  47. package/dist/events/interface.d.ts.map +1 -1
  48. package/dist/events/interface.js.map +1 -1
  49. package/dist/index.d.ts +14 -5
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +7 -2
  52. package/dist/index.js.map +1 -1
  53. package/dist/invites/schemas.d.ts +24 -0
  54. package/dist/invites/schemas.d.ts.map +1 -0
  55. package/dist/invites/schemas.js +17 -0
  56. package/dist/invites/schemas.js.map +1 -0
  57. package/dist/metrics/interface.d.ts +6 -0
  58. package/dist/metrics/interface.d.ts.map +1 -1
  59. package/dist/metrics/interface.js.map +1 -1
  60. package/dist/organizations/index.d.ts +4 -3
  61. package/dist/organizations/index.d.ts.map +1 -1
  62. package/dist/organizations/index.js +2 -1
  63. package/dist/organizations/index.js.map +1 -1
  64. package/dist/organizations/interface.d.ts +1 -1
  65. package/dist/organizations/interface.d.ts.map +1 -1
  66. package/dist/organizations/paths.d.ts +22 -0
  67. package/dist/organizations/paths.d.ts.map +1 -0
  68. package/dist/organizations/paths.js +43 -0
  69. package/dist/organizations/paths.js.map +1 -0
  70. package/dist/organizations/schemas.d.ts +10 -0
  71. package/dist/organizations/schemas.d.ts.map +1 -1
  72. package/dist/organizations/schemas.js +6 -0
  73. package/dist/organizations/schemas.js.map +1 -1
  74. package/dist/organizations/types.d.ts +10 -1
  75. package/dist/organizations/types.d.ts.map +1 -1
  76. package/dist/organizations.d.ts +93 -0
  77. package/dist/organizations.d.ts.map +1 -0
  78. package/dist/organizations.js +14 -0
  79. package/dist/organizations.js.map +1 -0
  80. package/dist/solveCache/interface.d.ts +86 -0
  81. package/dist/solveCache/interface.d.ts.map +1 -0
  82. package/dist/solveCache/interface.js +12 -0
  83. package/dist/solveCache/interface.js.map +1 -0
  84. package/dist/storage/assetClasses.d.ts +59 -0
  85. package/dist/storage/assetClasses.d.ts.map +1 -0
  86. package/dist/storage/assetClasses.js +86 -0
  87. package/dist/storage/assetClasses.js.map +1 -0
  88. package/dist/storage/cacheBust.d.ts +21 -0
  89. package/dist/storage/cacheBust.d.ts.map +1 -0
  90. package/dist/storage/cacheBust.js +29 -0
  91. package/dist/storage/cacheBust.js.map +1 -0
  92. package/dist/storage/image.d.ts +6 -1
  93. package/dist/storage/image.d.ts.map +1 -1
  94. package/dist/storage/image.js +8 -3
  95. package/dist/storage/image.js.map +1 -1
  96. package/dist/storage/index.d.ts +3 -0
  97. package/dist/storage/index.d.ts.map +1 -1
  98. package/dist/storage/index.js +2 -0
  99. package/dist/storage/index.js.map +1 -1
  100. package/dist/testing/adapterSuite.d.ts +33 -0
  101. package/dist/testing/adapterSuite.d.ts.map +1 -0
  102. package/dist/testing/adapterSuite.js +166 -0
  103. package/dist/testing/adapterSuite.js.map +1 -0
  104. package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
  105. package/dist/testing/suites/definitionStoreSuite.js +87 -0
  106. package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
  107. package/dist/testing/suites/eventSinkSuite.js +1 -0
  108. package/dist/testing/suites/eventSinkSuite.js.map +1 -1
  109. package/dist/testing/suites/orgStoreSuite.d.ts.map +1 -1
  110. package/dist/testing/suites/orgStoreSuite.js +12 -0
  111. package/dist/testing/suites/orgStoreSuite.js.map +1 -1
  112. package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
  113. package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
  114. package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
  115. package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
  116. package/dist/testing/suites/runner.d.ts +21 -0
  117. package/dist/testing/suites/runner.d.ts.map +1 -0
  118. package/dist/testing/suites/runner.js +7 -0
  119. package/dist/testing/suites/runner.js.map +1 -0
  120. package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
  121. package/dist/testing/suites/storageProviderSuite.js +18 -0
  122. package/dist/testing/suites/storageProviderSuite.js.map +1 -1
  123. package/package.json +9 -24
  124. package/src/access/index.ts +0 -23
  125. package/src/access/rules.ts +0 -276
  126. package/src/auth/index.ts +0 -8
  127. package/src/auth/interface.ts +0 -279
  128. package/src/auth/types.ts +0 -32
  129. package/src/bindings/index.ts +0 -2
  130. package/src/bindings/interface.ts +0 -61
  131. package/src/computeServer/index.ts +0 -17
  132. package/src/computeServer/interface.ts +0 -62
  133. package/src/computeServer/types.ts +0 -74
  134. package/src/computeServer/utils.ts +0 -112
  135. package/src/config.ts +0 -99
  136. package/src/context.ts +0 -66
  137. package/src/data/index.ts +0 -1
  138. package/src/data/interface.ts +0 -70
  139. package/src/definitions/index.ts +0 -26
  140. package/src/definitions/interface.ts +0 -78
  141. package/src/definitions/paths.ts +0 -31
  142. package/src/definitions/schemas.ts +0 -64
  143. package/src/definitions/types.ts +0 -111
  144. package/src/errors.ts +0 -16
  145. package/src/events/audit.ts +0 -67
  146. package/src/events/index.ts +0 -9
  147. package/src/events/interface.ts +0 -102
  148. package/src/index.ts +0 -219
  149. package/src/invites/index.ts +0 -2
  150. package/src/invites/interface.ts +0 -38
  151. package/src/invites/types.ts +0 -39
  152. package/src/metrics/interface.ts +0 -86
  153. package/src/organizations/index.ts +0 -14
  154. package/src/organizations/interface.ts +0 -68
  155. package/src/organizations/schemas.ts +0 -52
  156. package/src/organizations/types.ts +0 -31
  157. package/src/pagination.ts +0 -45
  158. package/src/permissions/index.ts +0 -3
  159. package/src/permissions/interface.ts +0 -60
  160. package/src/permissions/types.ts +0 -21
  161. package/src/platformProjects/index.ts +0 -3
  162. package/src/platformProjects/interface.ts +0 -27
  163. package/src/platformProjects/schemas.ts +0 -13
  164. package/src/platformProjects/types.ts +0 -22
  165. package/src/projects/index.ts +0 -4
  166. package/src/projects/interface.ts +0 -58
  167. package/src/projects/schemas.ts +0 -28
  168. package/src/projects/types.ts +0 -36
  169. package/src/shareLinks/index.ts +0 -5
  170. package/src/shareLinks/interface.ts +0 -35
  171. package/src/shareLinks/schemas.ts +0 -21
  172. package/src/shareLinks/types.ts +0 -34
  173. package/src/storage/image.ts +0 -77
  174. package/src/storage/index.ts +0 -9
  175. package/src/storage/interface.ts +0 -27
  176. package/src/testing/index.ts +0 -47
  177. package/src/testing/suites/authProviderSuite.ts +0 -207
  178. package/src/testing/suites/computeServerStoreSuite.ts +0 -195
  179. package/src/testing/suites/definitionStoreSuite.ts +0 -585
  180. package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
  181. package/src/testing/suites/eventSinkSuite.ts +0 -379
  182. package/src/testing/suites/helpers.ts +0 -87
  183. package/src/testing/suites/inviteStoreSuite.ts +0 -143
  184. package/src/testing/suites/orgStoreSuite.ts +0 -412
  185. package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
  186. package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
  187. package/src/testing/suites/projectStoreSuite.ts +0 -381
  188. package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
  189. package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
  190. package/src/testing/suites/storageProviderSuite.ts +0 -175
  191. package/src/testing/suites/userProfileStoreSuite.ts +0 -134
  192. package/src/userProfile/index.ts +0 -3
  193. package/src/userProfile/interface.ts +0 -48
  194. package/src/userProfile/types.ts +0 -24
  195. package/src/utils/audit.ts +0 -46
  196. 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;
@@ -1,3 +0,0 @@
1
- export type { PlatformPermission } from './types.js';
2
- export { PlatformPermissionSchema, ALL_PLATFORM_PERMISSIONS } from './types.js';
3
- export type { IPlatformPermissionStore } from './interface.js';
@@ -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
- }
@@ -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,3 +0,0 @@
1
- export type { PlatformProjectGrant, PlatformProjectGranteeType } from './types.js';
2
- export type { IPlatformProjectGrantStore } from './interface.js';
3
- export { PlatformProjectGrantSchema, PlatformProjectGranteeTypeSchema } from './schemas.js';
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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>;
@@ -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;
@@ -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
- }
@@ -1,9 +0,0 @@
1
- export type { IStorageProvider } from './interface.js';
2
- export {
3
- IMAGE_MAX_WIDTH,
4
- IMAGE_WEBP_QUALITY,
5
- isImageUpload,
6
- toWebpPath,
7
- transcodeImageIfNeeded
8
- } from './image.js';
9
- export type { TranscodeResult } from './image.js';
@@ -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
- }
@@ -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';