@tangle-network/agent-app 0.27.2 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/DesignCanvas-AOIVN3SJ.js +12 -0
  2. package/dist/DesignCanvasEditor-IB2FMBBD.js +13 -0
  3. package/dist/InviteAcceptPage-EUOGZO2X.js +7 -0
  4. package/dist/MembersPanel-4CGUQRPF.js +8 -0
  5. package/dist/MembersPanel-4CGUQRPF.js.map +1 -0
  6. package/dist/{TimelineEditor-JDG54OBP.js → TimelineEditor-Z7C6BFP5.js} +2 -2
  7. package/dist/TimelineEditor-Z7C6BFP5.js.map +1 -0
  8. package/dist/access-ChAHG4Tp.d.ts +539 -0
  9. package/dist/{apply-Cp8c3K9D.d.ts → apply-Gk4raT2j.d.ts} +3 -1
  10. package/dist/{chunk-AFDROJ64.js → chunk-2QI7XV2T.js} +1 -1
  11. package/dist/chunk-2QI7XV2T.js.map +1 -0
  12. package/dist/chunk-3G334FVA.js +162 -0
  13. package/dist/chunk-3G334FVA.js.map +1 -0
  14. package/dist/chunk-3SVAA3MA.js +41 -0
  15. package/dist/chunk-3SVAA3MA.js.map +1 -0
  16. package/dist/{chunk-LUE4HO5C.js → chunk-4IQMTTNE.js} +10 -10
  17. package/dist/chunk-4IQMTTNE.js.map +1 -0
  18. package/dist/chunk-535V6BH6.js +128 -0
  19. package/dist/chunk-535V6BH6.js.map +1 -0
  20. package/dist/chunk-63CE7FEZ.js +63 -0
  21. package/dist/chunk-63CE7FEZ.js.map +1 -0
  22. package/dist/{chunk-5RT6KY4G.js → chunk-GQRL6VNU.js} +1 -1
  23. package/dist/chunk-GQRL6VNU.js.map +1 -0
  24. package/dist/{chunk-SCG5JJ4C.js → chunk-HFC4BTWJ.js} +32 -4
  25. package/dist/chunk-HFC4BTWJ.js.map +1 -0
  26. package/dist/{chunk-7QCIYDGC.js → chunk-IOREXWLM.js} +2 -2
  27. package/dist/{chunk-6UOE5CTA.js → chunk-JT6HHO5P.js} +5 -10
  28. package/dist/chunk-JT6HHO5P.js.map +1 -0
  29. package/dist/{chunk-JZAJE3JL.js → chunk-M2EBUDZ7.js} +19 -5
  30. package/dist/chunk-M2EBUDZ7.js.map +1 -0
  31. package/dist/{chunk-HABVCJJT.js → chunk-N4ZFKQ5C.js} +271 -404
  32. package/dist/chunk-N4ZFKQ5C.js.map +1 -0
  33. package/dist/chunk-S5CAJX6O.js +14 -0
  34. package/dist/chunk-S5CAJX6O.js.map +1 -0
  35. package/dist/{chunk-RJ2ZHK5D.js → chunk-S6WS2B32.js} +132 -7
  36. package/dist/chunk-S6WS2B32.js.map +1 -0
  37. package/dist/chunk-SQV7NUEM.js +136 -0
  38. package/dist/chunk-SQV7NUEM.js.map +1 -0
  39. package/dist/chunk-UAKFCMWK.js +1 -0
  40. package/dist/chunk-UAKFCMWK.js.map +1 -0
  41. package/dist/chunk-V5XZKHCL.js +296 -0
  42. package/dist/chunk-V5XZKHCL.js.map +1 -0
  43. package/dist/chunk-XWJXZ6WE.js +604 -0
  44. package/dist/chunk-XWJXZ6WE.js.map +1 -0
  45. package/dist/chunk-Y2SZVW35.js +125 -0
  46. package/dist/chunk-Y2SZVW35.js.map +1 -0
  47. package/dist/{chunk-27LQXQSS.js → chunk-YEWTVDDT.js} +224 -767
  48. package/dist/chunk-YEWTVDDT.js.map +1 -0
  49. package/dist/{chunk-UDXMR3AD.js → chunk-YNALYIQ3.js} +56 -18
  50. package/dist/chunk-YNALYIQ3.js.map +1 -0
  51. package/dist/command-stack-CkjelfK_.d.ts +267 -0
  52. package/dist/design-canvas/drizzle.d.ts +2 -2
  53. package/dist/design-canvas/index.d.ts +7 -6
  54. package/dist/design-canvas/index.js +3 -2
  55. package/dist/design-canvas-react/engine.d.ts +349 -0
  56. package/dist/design-canvas-react/engine.js +86 -0
  57. package/dist/design-canvas-react/engine.js.map +1 -0
  58. package/dist/design-canvas-react/index.d.ts +69 -649
  59. package/dist/design-canvas-react/index.js +84 -173
  60. package/dist/design-canvas-react/index.js.map +1 -1
  61. package/dist/design-canvas-react/lazy.d.ts +6 -0
  62. package/dist/design-canvas-react/lazy.js +9 -0
  63. package/dist/design-canvas-react/lazy.js.map +1 -0
  64. package/dist/{export-presets-Dl5Aa5xj.d.ts → export-presets-DAsU5Wal.d.ts} +2 -143
  65. package/dist/flow-types-Cb_AblZs.d.ts +25 -0
  66. package/dist/index.d.ts +11 -8
  67. package/dist/index.js +21 -8
  68. package/dist/lazy-BHHqS8-j.d.ts +72 -0
  69. package/dist/missions/index.d.ts +12 -0
  70. package/dist/missions/index.js +1 -1
  71. package/dist/{model-BHLN208Z.d.ts → model-BBwyOuWG.d.ts} +3 -1
  72. package/dist/operations-DLIvvD00.d.ts +144 -0
  73. package/dist/platform/index.js +1 -1
  74. package/dist/roles-CLtYKHHl.d.ts +49 -0
  75. package/dist/runtime/index.d.ts +33 -33
  76. package/dist/runtime/index.js +1 -1
  77. package/dist/sequences/index.d.ts +1 -1
  78. package/dist/sequences/index.js +2 -1
  79. package/dist/sequences-react/index.d.ts +15 -1
  80. package/dist/sequences-react/index.js +2 -2
  81. package/dist/{store-CUStmtdH.d.ts → store-CFyxgTlD.d.ts} +1 -1
  82. package/dist/teams/drizzle.d.ts +42 -0
  83. package/dist/teams/drizzle.js +194 -0
  84. package/dist/teams/drizzle.js.map +1 -0
  85. package/dist/teams/index.d.ts +44 -0
  86. package/dist/teams/index.js +39 -0
  87. package/dist/teams/index.js.map +1 -0
  88. package/dist/teams/members-api.d.ts +152 -0
  89. package/dist/teams/members-api.js +261 -0
  90. package/dist/teams/members-api.js.map +1 -0
  91. package/dist/teams-react/index.d.ts +81 -0
  92. package/dist/teams-react/index.js +12 -0
  93. package/dist/teams-react/index.js.map +1 -0
  94. package/dist/teams-react/lazy.d.ts +9 -0
  95. package/dist/teams-react/lazy.js +13 -0
  96. package/dist/teams-react/lazy.js.map +1 -0
  97. package/dist/theme/index.d.ts +80 -0
  98. package/dist/theme/index.js +14 -0
  99. package/dist/theme/index.js.map +1 -0
  100. package/dist/theme/tailwind-preset.d.ts +65 -0
  101. package/dist/theme/tailwind-preset.js +33 -0
  102. package/dist/theme/tailwind-preset.js.map +1 -0
  103. package/dist/theme/tokens.css +95 -0
  104. package/dist/trace/index.d.ts +2 -18
  105. package/dist/trace/index.js +2 -2
  106. package/dist/web/index.d.ts +25 -2
  107. package/dist/web/index.js +3 -1
  108. package/dist/web-react/index.d.ts +71 -31
  109. package/dist/web-react/index.js +625 -486
  110. package/dist/web-react/index.js.map +1 -1
  111. package/package.json +53 -3
  112. package/dist/DesignCanvas-73IUJJUP.js +0 -10
  113. package/dist/DesignCanvasEditor-6F2UTOSW.js +0 -9
  114. package/dist/chunk-27LQXQSS.js.map +0 -1
  115. package/dist/chunk-5RT6KY4G.js.map +0 -1
  116. package/dist/chunk-6UOE5CTA.js.map +0 -1
  117. package/dist/chunk-AFDROJ64.js.map +0 -1
  118. package/dist/chunk-HABVCJJT.js.map +0 -1
  119. package/dist/chunk-JZAJE3JL.js.map +0 -1
  120. package/dist/chunk-LUE4HO5C.js.map +0 -1
  121. package/dist/chunk-RJ2ZHK5D.js.map +0 -1
  122. package/dist/chunk-SCG5JJ4C.js.map +0 -1
  123. package/dist/chunk-UDXMR3AD.js.map +0 -1
  124. /package/dist/{DesignCanvas-73IUJJUP.js.map → DesignCanvas-AOIVN3SJ.js.map} +0 -0
  125. /package/dist/{DesignCanvasEditor-6F2UTOSW.js.map → DesignCanvasEditor-IB2FMBBD.js.map} +0 -0
  126. /package/dist/{TimelineEditor-JDG54OBP.js.map → InviteAcceptPage-EUOGZO2X.js.map} +0 -0
  127. /package/dist/{chunk-7QCIYDGC.js.map → chunk-IOREXWLM.js.map} +0 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Pure role algebra for the teams capability — the tenancy/membership model
3
+ * shared across the fleet. Zero dependencies: no drizzle, no env, no react, no
4
+ * I/O. The DB layer (`./teams/drizzle`), the members API (`./teams/members-api`)
5
+ * and the React surface (`./teams-react`) all build on these functions; this
6
+ * leaf imports nothing back, so a consumer can pull just the role math.
7
+ *
8
+ * Two role ladders, deliberately distinct:
9
+ * - Organization roles rank the tenant (who owns/administers the org and its
10
+ * billing). An org owner/admin is an owner of every workspace under it.
11
+ * - Workspace roles rank a single workspace. They are the access primitive
12
+ * every route checks via `hasWorkspaceRole(actual, minimum)`.
13
+ *
14
+ * `resolveWorkspaceRole` is the bridge: it folds the org role and the
15
+ * per-workspace role into the one effective workspace role a request runs at.
16
+ */
17
+ declare const WORKSPACE_ROLES: readonly ["viewer", "editor", "admin", "owner"];
18
+ type WorkspaceRole = typeof WORKSPACE_ROLES[number];
19
+ declare const ASSIGNABLE_WORKSPACE_ROLES: readonly ["viewer", "editor", "admin"];
20
+ type AssignableWorkspaceRole = typeof ASSIGNABLE_WORKSPACE_ROLES[number];
21
+ declare const ORGANIZATION_ROLES: readonly ["owner", "admin", "member", "billing"];
22
+ type OrganizationRole = typeof ORGANIZATION_ROLES[number];
23
+ type WorkspaceCollaborationAccess = 'read' | 'write';
24
+ type SandboxWorkspaceRole = 'owner' | 'admin' | 'developer' | 'viewer';
25
+ declare const WORKSPACE_ROLE_RANK: Record<WorkspaceRole, number>;
26
+ declare const ORGANIZATION_ROLE_RANK: Record<OrganizationRole, number>;
27
+ /** True when `actual` is at least `minimum` on the workspace ladder. */
28
+ declare function hasWorkspaceRole(actual: WorkspaceRole, minimum: WorkspaceRole): boolean;
29
+ /** True when `actual` is at least `minimum` on the organization ladder. */
30
+ declare function hasOrganizationRole(actual: OrganizationRole, minimum: OrganizationRole): boolean;
31
+ declare function isAssignableWorkspaceRole(value: unknown): value is AssignableWorkspaceRole;
32
+ /** Org owners and admins are workspace owners across the whole org. */
33
+ declare function organizationRoleGrantsWorkspaceOwner(role: OrganizationRole | string | null | undefined): boolean;
34
+ /**
35
+ * The effective workspace role a request runs at: org owner/admin → owner of
36
+ * every workspace; otherwise the explicit per-workspace role (or null = no
37
+ * access). This is the single fold every access check goes through.
38
+ */
39
+ declare function resolveWorkspaceRole(organizationRole: OrganizationRole | string | null | undefined, workspaceRole: WorkspaceRole | null | undefined): WorkspaceRole | null;
40
+ /**
41
+ * Whether `actorRole` may set/clear a member currently at `targetRole`. Owners
42
+ * can manage anyone; everyone else can only manage members strictly below
43
+ * their own rank (an admin cannot demote another admin or an owner).
44
+ */
45
+ declare function canManageWorkspaceMemberRole(actorRole: WorkspaceRole, targetRole: WorkspaceRole): boolean;
46
+ declare function workspaceRoleToCollaborationAccess(role: WorkspaceRole): WorkspaceCollaborationAccess;
47
+ declare function workspaceRoleToSandboxRole(role: WorkspaceRole): SandboxWorkspaceRole;
48
+
49
+ export { ASSIGNABLE_WORKSPACE_ROLES as A, type OrganizationRole as O, type SandboxWorkspaceRole as S, WORKSPACE_ROLES as W, type AssignableWorkspaceRole as a, ORGANIZATION_ROLES as b, ORGANIZATION_ROLE_RANK as c, WORKSPACE_ROLE_RANK as d, type WorkspaceCollaborationAccess as e, type WorkspaceRole as f, canManageWorkspaceMemberRole as g, hasOrganizationRole as h, hasWorkspaceRole as i, isAssignableWorkspaceRole as j, workspaceRoleToSandboxRole as k, organizationRoleGrantsWorkspaceOwner as o, resolveWorkspaceRole as r, workspaceRoleToCollaborationAccess as w };
@@ -1,13 +1,44 @@
1
+ export { C as CatalogModel, M as ModelCatalog, R as RouterModel, _ as __resetCatalogCache, b as buildCatalog, f as fetchModelCatalog, n as normalizeModelId } from '../model-catalog-BEAEVDaa.js';
2
+ export { C as CreateTangleRouterModelConfigOptions, D as DEFAULT_TANGLE_BILLING_ENFORCEMENT_ENV_VAR, a as DEFAULT_TANGLE_ROUTER_BASE_URL, R as ResolveModelOptions, b as ResolveUserTangleExecutionKeyForUserOptions, c as ResolveUserTangleExecutionKeyOptions, d as ResolvedTangleExecutionKey, T as TangleBillingEnforcementOptions, e as TangleExecutionEnvironment, f as TangleExecutionKeyError, g as TangleExecutionKeyErrorCode, h as TangleExecutionKeyHttpError, i as TangleExecutionKeySource, j as TangleModelConfig, k as createTangleRouterModelConfig, l as isTangleBillingEnforcementDisabled, m as isTangleExecutionKeyError, r as resolveTangleExecutionEnvironment, n as resolveTangleModelConfig, o as resolveUserTangleExecutionKey, p as resolveUserTangleExecutionKeyForUser, t as tangleExecutionKeyHttpError } from '../model-CKzniMMr.js';
1
3
  import * as _tangle_network_agent_runtime from '@tangle-network/agent-runtime';
2
4
  import { ToolLoopMessage, ToolLoopResult, StreamToolLoopYield, ToolLoopCall } from '@tangle-network/agent-runtime';
3
5
  export { RunToolLoopOptions as AppToolLoopOptions, ToolLoopAssistantToolCall as LoopAssistantToolCall, ToolLoopMessage as LoopMessage, ToolLoopCall as LoopToolCall, StreamToolLoopOptions as StreamAppToolLoopOptions, StreamToolLoopYield as StreamLoopYield, ToolLoopEvent, ToolLoopResult, ToolLoopStopReason, runToolLoop as runAppToolLoop, streamToolLoop as streamAppToolLoop } from '@tangle-network/agent-runtime';
4
- export { C as CatalogModel, M as ModelCatalog, R as RouterModel, _ as __resetCatalogCache, b as buildCatalog, f as fetchModelCatalog, n as normalizeModelId } from '../model-catalog-BEAEVDaa.js';
5
- export { C as CreateTangleRouterModelConfigOptions, D as DEFAULT_TANGLE_BILLING_ENFORCEMENT_ENV_VAR, a as DEFAULT_TANGLE_ROUTER_BASE_URL, R as ResolveModelOptions, b as ResolveUserTangleExecutionKeyForUserOptions, c as ResolveUserTangleExecutionKeyOptions, d as ResolvedTangleExecutionKey, T as TangleBillingEnforcementOptions, e as TangleExecutionEnvironment, f as TangleExecutionKeyError, g as TangleExecutionKeyErrorCode, h as TangleExecutionKeyHttpError, i as TangleExecutionKeySource, j as TangleModelConfig, k as createTangleRouterModelConfig, l as isTangleBillingEnforcementDisabled, m as isTangleExecutionKeyError, r as resolveTangleExecutionEnvironment, n as resolveTangleModelConfig, o as resolveUserTangleExecutionKey, p as resolveUserTangleExecutionKeyForUser, t as tangleExecutionKeyHttpError } from '../model-CKzniMMr.js';
6
6
  import { b as AppToolContext, e as AppToolProducedEvent, f as AppToolTaxonomy, c as AppToolHandlers, d as AppToolOutcome } from '../types-2rOJo8Hc.js';
7
7
  import { CertifiedProfile } from '@tangle-network/agent-runtime/intelligence';
8
8
  import { A as AppToolMcpServer } from '../mcp-BShTlESm.js';
9
9
  import '../auth-BlS9GWfL.js';
10
10
 
11
+ /**
12
+ * Events the app's OpenAI-compat stream adapter ({@link toLoopEvents}) yields.
13
+ *
14
+ * This is the app's own `Raw` event type for the streaming loop — the canonical
15
+ * `streamToolLoop<Raw>` is generic over it. It widens the substrate's
16
+ * tool-loop event with `reasoning` (DeepSeek/router `reasoning_content` /
17
+ * `thinking` deltas, rendered as thinking sections) and `usage` (per-message
18
+ * token accounting) — neither belongs in the substrate's loop contract, so they
19
+ * stay here. The adapter maps each into the `streamTurn` seam; `text` and
20
+ * `tool_call` drive the loop, `reasoning` / `usage` pass through to the UI.
21
+ */
22
+ type LoopEvent = {
23
+ type: 'text';
24
+ text: string;
25
+ } | {
26
+ type: 'reasoning';
27
+ text: string;
28
+ } | {
29
+ type: 'tool_call';
30
+ call: _tangle_network_agent_runtime.ToolLoopCall;
31
+ } | {
32
+ type: 'usage';
33
+ usage: {
34
+ promptTokens: number;
35
+ completionTokens: number;
36
+ };
37
+ } | {
38
+ type: 'other';
39
+ event: unknown;
40
+ };
41
+
11
42
  /**
12
43
  * OpenAI-compatible stream → `LoopEvent` adapter, for NON-sandbox copilots.
13
44
  *
@@ -356,35 +387,4 @@ interface SurfaceMergeBase {
356
387
  */
357
388
  declare function mergeSurfaceOverlay<TBase extends SurfaceMergeBase>(base: TBase, overlay: SurfaceOverlay): TBase & SurfaceMergeBase;
358
389
 
359
- /**
360
- * Events the app's OpenAI-compat stream adapter ({@link toLoopEvents}) yields.
361
- *
362
- * This is the app's own `Raw` event type for the streaming loop — the canonical
363
- * `streamToolLoop<Raw>` is generic over it. It widens the substrate's
364
- * tool-loop event with `reasoning` (DeepSeek/router `reasoning_content` /
365
- * `thinking` deltas, rendered as thinking sections) and `usage` (per-message
366
- * token accounting) — neither belongs in the substrate's loop contract, so they
367
- * stay here. The adapter maps each into the `streamTurn` seam; `text` and
368
- * `tool_call` drive the loop, `reasoning` / `usage` pass through to the UI.
369
- */
370
- type LoopEvent = {
371
- type: 'text';
372
- text: string;
373
- } | {
374
- type: 'reasoning';
375
- text: string;
376
- } | {
377
- type: 'tool_call';
378
- call: _tangle_network_agent_runtime.ToolLoopCall;
379
- } | {
380
- type: 'usage';
381
- usage: {
382
- promptTokens: number;
383
- completionTokens: number;
384
- };
385
- } | {
386
- type: 'other';
387
- event: unknown;
388
- };
389
-
390
390
  export { type AgentRuntime, type AgentRuntimeModelConfig, type AgentTurnOptions, type AnySurfaceKind, type CertifiedDelivery, type CertifiedDeliveryConfig, type CreateAgentRuntimeOptions, type LoopEvent, type OpenAICompatStreamTurnOptions, type OpenAIStreamChunk, type ResolvedAgentProfile, type SurfaceKindDefinition, type SurfaceMcpServer, type SurfaceMergeBase, type SurfaceOverlay, type SurfacePermissionValue, type SurfaceRegistry, createAgentRuntime, createCertifiedDelivery, createOpenAICompatStreamTurn, createSurfaceRegistry, defineSurfaceKind, mergeSurfaceOverlay, toLoopEvents };
@@ -12,7 +12,7 @@ import {
12
12
  runToolLoop,
13
13
  streamToolLoop,
14
14
  toLoopEvents
15
- } from "../chunk-LUE4HO5C.js";
15
+ } from "../chunk-4IQMTTNE.js";
16
16
  import {
17
17
  DEFAULT_TANGLE_BILLING_ENFORCEMENT_ENV_VAR,
18
18
  DEFAULT_TANGLE_ROUTER_BASE_URL,
@@ -1,6 +1,6 @@
1
1
  import { j as SequenceMediaKind, o as SequenceTimeline, r as TimelineInterval, m as SequenceStore } from '../store-gckrNq-g.js';
2
2
  export { M as MIN_SEQUENCE_CLIP_FRAMES, N as NewSequenceClip, a as NewSequenceDecision, b as NewSequenceTrack, S as SequenceClip, c as SequenceClipMedia, d as SequenceClipPatch, e as SequenceDecision, f as SequenceExportFormat, g as SequenceExportRecord, h as SequenceExportStatus, i as SequenceFrameSnapshot, k as SequenceMeta, l as SequenceStatus, n as SequenceStoreScope, p as SequenceTrack, q as SequenceTrackKind, T as TimelineClipBounds, s as assertClipFitsSequence, t as chooseCaptionPlacement, u as clampClipDuration, v as clampClipStart, w as formatSeconds, x as formatTimecode, y as framesToSeconds, z as secondsToFrames, A as snapshotFrame, B as trackIntervals } from '../store-gckrNq-g.js';
3
- export { A as AddCaptionOperation, C as CaptionTargetResolution, a as CreateTrackOperation, D as DeleteClipOperation, E as ExtendSequenceOperation, M as MoveClipOperation, P as PlaceClipOperation, Q as QueueExportOperation, S as SEQUENCE_OPERATION_TYPES, b as SequenceApplyResult, c as SequenceOperation, d as SequenceOperationContext, e as SequenceOperationType, f as SequencePlan, g as SetClipDisabledOperation, h as SetClipTextOperation, i as SplitClipOperation, T as TrimClipOperation, j as applySequenceOperation, k as applySequenceOperations, l as assertSequenceMediaUrl, m as captionTrackNameForLanguage, n as lastClipEndFrame, p as parseSequenceOperations, r as resolveCaptionPlacement, o as resolveCaptionTarget, q as resolvePlaceClipTrack, v as validateAddCaption, s as validateCreateTrack, t as validateDeleteClip, u as validateExtendSequence, w as validateMoveClip, x as validatePlaceClip, y as validateQueueExport, z as validateSequenceOperation, B as validateSequenceOperations, F as validateSetClipDisabled, G as validateSetClipText, H as validateSplitClip, I as validateTrimClip } from '../apply-Cp8c3K9D.js';
3
+ export { A as AddCaptionOperation, C as CaptionTargetResolution, a as CreateTrackOperation, D as DeleteClipOperation, E as ExtendSequenceOperation, M as MoveClipOperation, P as PlaceClipOperation, Q as QueueExportOperation, S as SEQUENCE_OPERATION_TYPES, b as SequenceApplyResult, c as SequenceOperation, d as SequenceOperationContext, e as SequenceOperationType, f as SequencePlan, g as SetClipDisabledOperation, h as SetClipTextOperation, i as SplitClipOperation, T as TrimClipOperation, j as applySequenceOperation, k as applySequenceOperations, l as assertSequenceMediaUrl, m as captionTrackNameForLanguage, n as lastClipEndFrame, p as parseSequenceOperations, r as resolveCaptionPlacement, o as resolveCaptionTarget, q as resolvePlaceClipTrack, v as validateAddCaption, s as validateCreateTrack, t as validateDeleteClip, u as validateExtendSequence, w as validateMoveClip, x as validatePlaceClip, y as validateQueueExport, z as validateSequenceOperation, B as validateSequenceOperations, F as validateSetClipDisabled, G as validateSetClipText, H as validateSplitClip, I as validateTrimClip } from '../apply-Gk4raT2j.js';
4
4
  import { c as ToolHeaderNames } from '../auth-BlS9GWfL.js';
5
5
  import { A as AppToolMcpServer } from '../mcp-BShTlESm.js';
6
6
  import { b as AppToolContext } from '../types-2rOJo8Hc.js';
@@ -41,7 +41,7 @@ import {
41
41
  validateSetClipText,
42
42
  validateSplitClip,
43
43
  validateTrimClip
44
- } from "../chunk-6UOE5CTA.js";
44
+ } from "../chunk-JT6HHO5P.js";
45
45
  import {
46
46
  MIN_SEQUENCE_CLIP_FRAMES,
47
47
  assertClipFitsSequence,
@@ -55,6 +55,7 @@ import {
55
55
  snapshotFrame,
56
56
  trackIntervals
57
57
  } from "../chunk-ZYBWGSAZ.js";
58
+ import "../chunk-HFC4BTWJ.js";
58
59
  import "../chunk-A76ZHWNF.js";
59
60
  export {
60
61
  DEFAULT_SEQUENCES_MCP_DESCRIPTION,
@@ -1,4 +1,4 @@
1
- import { c as SequenceOperation, b as SequenceApplyResult } from '../apply-Cp8c3K9D.js';
1
+ import { c as SequenceOperation, b as SequenceApplyResult } from '../apply-Gk4raT2j.js';
2
2
  import { o as SequenceTimeline, S as SequenceClip, p as SequenceTrack } from '../store-gckrNq-g.js';
3
3
  import * as react from 'react';
4
4
  import react__default from 'react';
@@ -543,6 +543,9 @@ interface TimelineClipChipProps {
543
543
  sequenceDurationFrames: number;
544
544
  selected: boolean;
545
545
  canWrite: boolean;
546
+ /** Roving-tabindex: exactly one chip per editor carries tabIndex 0 so the
547
+ * clip set is a single Tab stop; arrows move focus across the others. */
548
+ tabbable: boolean;
546
549
  frameProvider: VideoFrameProvider;
547
550
  /** Snap a candidate move (both clip edges considered); editor closes over
548
551
  * the engine's snap points. */
@@ -564,6 +567,12 @@ interface TimelineClipChipProps {
564
567
  };
565
568
  onSnapPointChange(point: SnapPoint | null): void;
566
569
  onSelect(clipId: string, additive: boolean): void;
570
+ /** Keyboard delete of the focused clip (one command on the stack), routed
571
+ * through the same locked-track guard as the transport Delete key. */
572
+ onRequestDelete(clipId: string): void;
573
+ /** Move keyboard focus to the previous/next clip in DOM order (roving
574
+ * tabindex); the editor owns the ordered chip set. */
575
+ onFocusStep(clipId: string, direction: -1 | 1): void;
567
576
  onCommitMove(input: ClipMoveCommit): void;
568
577
  onCommitTrim(input: ClipTrimCommit): void;
569
578
  onCommitText(input: {
@@ -580,6 +589,9 @@ interface TimelineTrackRowProps {
580
589
  zoom: number;
581
590
  sequenceDurationFrames: number;
582
591
  selectedClipIds: ReadonlySet<string>;
592
+ /** The single clip that carries tabIndex 0 (roving tabindex); null seeds the
593
+ * first chip in the editor as the lone Tab stop. */
594
+ tabbableClipId: string | null;
583
595
  canWrite: boolean;
584
596
  frameProvider: VideoFrameProvider;
585
597
  snapMove(candidate: {
@@ -599,6 +611,8 @@ interface TimelineTrackRowProps {
599
611
  };
600
612
  onSnapPointChange(point: SnapPoint | null): void;
601
613
  onSelectClip(clipId: string, additive: boolean): void;
614
+ onRequestDeleteClip(clipId: string): void;
615
+ onFocusStepClip(clipId: string, direction: -1 | 1): void;
602
616
  onCommitMove(input: ClipMoveCommit): void;
603
617
  onCommitTrim(input: ClipTrimCommit): void;
604
618
  onCommitText(input: {
@@ -47,7 +47,7 @@ import {
47
47
  trimClipCommand,
48
48
  trimEndDrag,
49
49
  trimStartDrag
50
- } from "../chunk-RJ2ZHK5D.js";
50
+ } from "../chunk-S6WS2B32.js";
51
51
  import "../chunk-ZYBWGSAZ.js";
52
52
 
53
53
  // src/sequences-react/media/transcription.ts
@@ -182,7 +182,7 @@ function createWhisperTranscriptionProvider(opts) {
182
182
 
183
183
  // src/sequences-react/lazy.tsx
184
184
  import React from "react";
185
- var SequenceTimelineEditorLazy = React.lazy(() => import("../TimelineEditor-JDG54OBP.js"));
185
+ var SequenceTimelineEditorLazy = React.lazy(() => import("../TimelineEditor-Z7C6BFP5.js"));
186
186
  export {
187
187
  COMMAND_HISTORY_LIMIT,
188
188
  DEFAULT_MAX_MEDIA_ELEMENTS,
@@ -1,4 +1,4 @@
1
- import { c as SceneDocument } from './model-BHLN208Z.js';
1
+ import { c as SceneDocument } from './model-BBwyOuWG.js';
2
2
 
3
3
  /**
4
4
  * Storage contract for design-canvas documents. Unlike sequences (row per
@@ -0,0 +1,42 @@
1
+ import { T as TeamDatabase, a as TeamTables, O as OrganizationRow, b as OrganizationMemberRow } from '../access-ChAHG4Tp.js';
2
+ export { C as CreateAccessOptions, c as CreateOrganizationAccessOptions, d as CreateTeamTablesOptions, e as OrganizationAccess, f as OrganizationAccessApi, g as TeamParentTable, U as UserWorkspaceSummary, W as WorkspaceAccess, h as WorkspaceAccessApi, i as WorkspaceAccessTable, j as WorkspaceMemberRow, k as createOrganizationAccess, l as createTeamTables, m as createWorkspaceAccess } from '../access-ChAHG4Tp.js';
3
+ import { O as OrganizationRole } from '../roles-CLtYKHHl.js';
4
+ import 'drizzle-orm/sqlite-core';
5
+
6
+ /**
7
+ * `ensurePersonalOrganization` — the substrate that makes solo-user adoption
8
+ * work. Every user gets exactly one auto-created `kind: 'personal'` org with
9
+ * the user as `owner` member. That org is the tenant that owns their
10
+ * workspaces, so role resolution (`getWorkspaceAccess`) has an org row to read
11
+ * even before any team/invite exists.
12
+ *
13
+ * Idempotent by construction: the org `slug` is derived from the user id
14
+ * (`personal-<id>`) and upserted; the membership upserts on the
15
+ * (organizationId, userId) unique index. Concurrent calls converge — no
16
+ * duplicate personal orgs, no duplicate owner rows.
17
+ *
18
+ * ADOPTION CONTRACT: call this once per user at first authenticated entry
19
+ * (e.g. right after sign-in / session bootstrap) BEFORE the first
20
+ * `getWorkspaceAccess`. Role resolution requires the org membership to exist;
21
+ * an adopter that skips this will see `null` access for a brand-new user with
22
+ * no org row. There is no implicit creation inside the access builders — that
23
+ * keeps reads side-effect-free; provisioning is this explicit call.
24
+ */
25
+
26
+ interface EnsurePersonalOrganizationUser {
27
+ id: string;
28
+ name?: string | null;
29
+ email?: string | null;
30
+ }
31
+ interface PersonalOrganizationResult {
32
+ organization: OrganizationRow;
33
+ member: OrganizationMemberRow;
34
+ role: OrganizationRole;
35
+ }
36
+ interface CreatePersonalOrganizationOptions {
37
+ db: TeamDatabase;
38
+ tables: TeamTables;
39
+ }
40
+ declare function createEnsurePersonalOrganization(opts: CreatePersonalOrganizationOptions): (user: EnsurePersonalOrganizationUser) => Promise<PersonalOrganizationResult>;
41
+
42
+ export { type CreatePersonalOrganizationOptions, type EnsurePersonalOrganizationUser, OrganizationMemberRow, OrganizationRow, type PersonalOrganizationResult, TeamDatabase, TeamTables, createEnsurePersonalOrganization };
@@ -0,0 +1,194 @@
1
+ import {
2
+ hasOrganizationRole,
3
+ hasWorkspaceRole,
4
+ resolveWorkspaceRole
5
+ } from "../chunk-63CE7FEZ.js";
6
+
7
+ // src/teams/drizzle/schema.ts
8
+ import { sql } from "drizzle-orm";
9
+ import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
10
+ var hexId = () => text("id").primaryKey().default(sql`(lower(hex(randomblob(16))))`);
11
+ var createdAt = () => integer("created_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`);
12
+ var updatedAt = () => integer("updated_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`);
13
+ function createTeamTables(opts) {
14
+ const { userTable, workspaceTable } = opts;
15
+ const organizations = sqliteTable("organization", {
16
+ id: hexId(),
17
+ name: text("name").notNull(),
18
+ slug: text("slug").notNull().unique(),
19
+ kind: text("kind", { enum: ["personal", "team"] }).notNull().default("personal"),
20
+ createdBy: text("created_by").notNull().references(() => userTable.id, { onDelete: "cascade" }),
21
+ createdAt: createdAt(),
22
+ updatedAt: updatedAt()
23
+ }, (table) => [
24
+ index("idx_organization_created_by").on(table.createdBy)
25
+ ]);
26
+ const organizationMembers = sqliteTable("organization_member", {
27
+ id: hexId(),
28
+ organizationId: text("organization_id").notNull().references(() => organizations.id, { onDelete: "cascade" }),
29
+ userId: text("user_id").notNull().references(() => userTable.id, { onDelete: "cascade" }),
30
+ role: text("role", { enum: ["owner", "admin", "member", "billing"] }).notNull().default("member"),
31
+ createdAt: createdAt(),
32
+ updatedAt: updatedAt()
33
+ }, (table) => [
34
+ uniqueIndex("uniq_org_member_user").on(table.organizationId, table.userId),
35
+ index("idx_org_member_user").on(table.userId)
36
+ ]);
37
+ const workspaceMembers = sqliteTable("workspace_member", {
38
+ id: hexId(),
39
+ workspaceId: text("workspace_id").notNull().references(() => workspaceTable.id, { onDelete: "cascade" }),
40
+ organizationMemberId: text("organization_member_id").references(() => organizationMembers.id, { onDelete: "cascade" }),
41
+ userId: text("user_id").references(() => userTable.id, { onDelete: "cascade" }),
42
+ role: text("role", { enum: ["owner", "admin", "editor", "viewer"] }).notNull().default("editor"),
43
+ invitedBy: text("invited_by"),
44
+ inviteEmail: text("invite_email"),
45
+ inviteToken: text("invite_token"),
46
+ invitedAt: integer("invited_at", { mode: "timestamp" }).notNull().default(sql`(unixepoch())`),
47
+ acceptedAt: integer("accepted_at", { mode: "timestamp" })
48
+ }, (table) => [
49
+ uniqueIndex("uniq_workspace_member_org_member").on(table.workspaceId, table.organizationMemberId),
50
+ index("idx_workspace_member_user").on(table.workspaceId, table.userId),
51
+ index("idx_member_user").on(table.userId),
52
+ uniqueIndex("idx_member_invite_token").on(table.inviteToken)
53
+ ]);
54
+ return { organizations, organizationMembers, workspaceMembers };
55
+ }
56
+
57
+ // src/teams/drizzle/access.ts
58
+ import { and, desc, eq, isNotNull } from "drizzle-orm";
59
+ function createWorkspaceAccess(opts) {
60
+ const { db, tables, workspaceTable } = opts;
61
+ const { organizations, organizationMembers, workspaceMembers } = tables;
62
+ const workspaces = workspaceTable;
63
+ async function getWorkspaceAccess(workspaceId, userId, minRole = "viewer") {
64
+ const [row] = await db.select({
65
+ workspace: workspaces,
66
+ organization: organizations,
67
+ organizationMember: organizationMembers,
68
+ projectRole: workspaceMembers.role,
69
+ acceptedAt: workspaceMembers.acceptedAt
70
+ }).from(workspaces).innerJoin(organizations, eq(organizations.id, workspaces.organizationId)).innerJoin(organizationMembers, and(
71
+ eq(organizationMembers.organizationId, workspaces.organizationId),
72
+ eq(organizationMembers.userId, userId)
73
+ )).leftJoin(workspaceMembers, and(
74
+ eq(workspaceMembers.workspaceId, workspaces.id),
75
+ eq(workspaceMembers.organizationMemberId, organizationMembers.id),
76
+ isNotNull(workspaceMembers.acceptedAt)
77
+ )).where(eq(workspaces.id, workspaceId)).limit(1);
78
+ if (!row) return null;
79
+ const role = resolveWorkspaceRole(row.organizationMember.role, row.projectRole);
80
+ if (!role) return null;
81
+ if (!hasWorkspaceRole(role, minRole)) return null;
82
+ return {
83
+ workspace: row.workspace,
84
+ organization: row.organization,
85
+ organizationMember: row.organizationMember,
86
+ role
87
+ };
88
+ }
89
+ async function requireWorkspaceAccess(workspaceId, userId, minRole = "viewer") {
90
+ const access = await getWorkspaceAccess(workspaceId, userId, minRole);
91
+ if (!access) throw new Response("Workspace not found", { status: 404 });
92
+ return access;
93
+ }
94
+ async function listUserWorkspaces(userId) {
95
+ const rows = await db.select({
96
+ workspace: workspaces,
97
+ organization: organizations,
98
+ organizationMember: organizationMembers,
99
+ projectRole: workspaceMembers.role
100
+ }).from(workspaces).innerJoin(organizations, eq(organizations.id, workspaces.organizationId)).innerJoin(organizationMembers, and(
101
+ eq(organizationMembers.organizationId, workspaces.organizationId),
102
+ eq(organizationMembers.userId, userId)
103
+ )).leftJoin(workspaceMembers, and(
104
+ eq(workspaceMembers.workspaceId, workspaces.id),
105
+ eq(workspaceMembers.organizationMemberId, organizationMembers.id),
106
+ isNotNull(workspaceMembers.acceptedAt)
107
+ )).orderBy(desc(workspaces.updatedAt));
108
+ return rows.flatMap((row) => {
109
+ const role = resolveWorkspaceRole(row.organizationMember.role, row.projectRole);
110
+ if (!role) return [];
111
+ return [{
112
+ ...row.workspace,
113
+ organizationId: row.organization.id,
114
+ organizationName: row.organization.name,
115
+ role
116
+ }];
117
+ });
118
+ }
119
+ return { getWorkspaceAccess, requireWorkspaceAccess, listUserWorkspaces };
120
+ }
121
+ function createOrganizationAccess(opts) {
122
+ const { db, tables } = opts;
123
+ const { organizations, organizationMembers } = tables;
124
+ async function getOrganizationAccess(organizationId, userId, minRole = "member") {
125
+ const [row] = await db.select({
126
+ organization: organizations,
127
+ member: organizationMembers
128
+ }).from(organizationMembers).innerJoin(organizations, eq(organizations.id, organizationMembers.organizationId)).where(and(
129
+ eq(organizationMembers.organizationId, organizationId),
130
+ eq(organizationMembers.userId, userId)
131
+ )).limit(1);
132
+ if (!row) return null;
133
+ const role = row.member.role;
134
+ if (!hasOrganizationRole(role, minRole)) return null;
135
+ return { organization: row.organization, member: row.member, role };
136
+ }
137
+ async function requireOrganizationAccess(organizationId, userId, minRole = "member") {
138
+ const access = await getOrganizationAccess(organizationId, userId, minRole);
139
+ if (!access) throw new Response("Organization not found", { status: 404 });
140
+ return access;
141
+ }
142
+ return { getOrganizationAccess, requireOrganizationAccess };
143
+ }
144
+
145
+ // src/teams/drizzle/personal-organization.ts
146
+ import { and as and2, eq as eq2 } from "drizzle-orm";
147
+ function createEnsurePersonalOrganization(opts) {
148
+ const { db, tables } = opts;
149
+ const { organizations, organizationMembers } = tables;
150
+ return async function ensurePersonalOrganization(user) {
151
+ const [existing] = await db.select({
152
+ organization: organizations,
153
+ member: organizationMembers
154
+ }).from(organizationMembers).innerJoin(organizations, eq2(organizations.id, organizationMembers.organizationId)).where(and2(
155
+ eq2(organizationMembers.userId, user.id),
156
+ eq2(organizations.kind, "personal")
157
+ )).limit(1);
158
+ if (existing) {
159
+ return {
160
+ organization: existing.organization,
161
+ member: existing.member,
162
+ role: existing.member.role
163
+ };
164
+ }
165
+ const orgName = user.name?.trim() || user.email?.split("@")[0] || "Personal";
166
+ const slug = `personal-${user.id}`;
167
+ const [organization] = await db.insert(organizations).values({
168
+ name: `${orgName}'s Organization`,
169
+ slug,
170
+ kind: "personal",
171
+ createdBy: user.id
172
+ }).onConflictDoUpdate({
173
+ target: organizations.slug,
174
+ set: { updatedAt: /* @__PURE__ */ new Date() }
175
+ }).returning();
176
+ const org = organization;
177
+ const [member] = await db.insert(organizationMembers).values({
178
+ organizationId: org.id,
179
+ userId: user.id,
180
+ role: "owner"
181
+ }).onConflictDoUpdate({
182
+ target: [organizationMembers.organizationId, organizationMembers.userId],
183
+ set: { role: "owner", updatedAt: /* @__PURE__ */ new Date() }
184
+ }).returning();
185
+ return { organization: org, member, role: "owner" };
186
+ };
187
+ }
188
+ export {
189
+ createEnsurePersonalOrganization,
190
+ createOrganizationAccess,
191
+ createTeamTables,
192
+ createWorkspaceAccess
193
+ };
194
+ //# sourceMappingURL=drizzle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/teams/drizzle/schema.ts","../../src/teams/drizzle/access.ts","../../src/teams/drizzle/personal-organization.ts"],"sourcesContent":["/**\n * Drizzle schema factory for the teams tables. The product owns the user and\n * workspace tables; this factory creates the tenancy/membership tables and\n * wires their foreign keys into the passed-in tables so the whole graph lives\n * in one drizzle schema with real cascade semantics. Column names, types,\n * defaults, enums, and indexes mirror gtm's hand-rolled tables so a product\n * with those tables can adopt the factory without rewriting rows.\n *\n * The three tables and their roles:\n * - `organization` — the TENANT/ownership primitive. `kind` is 'personal'\n * (one auto-created per user; see ensurePersonalOrganization) or 'team'.\n * This is what owns workspaces and what billing/seats attach to.\n * - `organizationMember` — who belongs to an org and at what org role.\n * - `workspaceMember` — the additive invite/member surface: per-workspace\n * access grants, including pending email-only invites carrying a token.\n *\n * Imports `drizzle-orm` at module top — that is WHY this lives behind the\n * `/teams/drizzle` sub-subpath. The pure `./teams` leaf imports none of this,\n * so a consumer that never touches the DB never pulls the optional peer.\n */\n\nimport { sql } from 'drizzle-orm'\nimport { index, integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'\nimport type { AnySQLiteColumn, AnySQLiteTable } from 'drizzle-orm/sqlite-core'\n\n/** A product table referenced by FK — only the `id` column is touched. */\nexport type TeamParentTable = AnySQLiteTable & { id: AnySQLiteColumn }\n\nexport interface CreateTeamTablesOptions {\n /** The product's user table — org/member rows reference `userTable.id`. */\n userTable: TeamParentTable\n /** The product's workspace table — workspace members reference `workspaceTable.id`. */\n workspaceTable: TeamParentTable\n}\n\nconst hexId = () => text('id').primaryKey().default(sql`(lower(hex(randomblob(16))))`)\n\nconst createdAt = () => integer('created_at', { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`)\n\nconst updatedAt = () => integer('updated_at', { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`)\n\nexport function createTeamTables(opts: CreateTeamTablesOptions) {\n const { userTable, workspaceTable } = opts\n\n const organizations = sqliteTable('organization', {\n id: hexId(),\n name: text('name').notNull(),\n slug: text('slug').notNull().unique(),\n kind: text('kind', { enum: ['personal', 'team'] }).notNull().default('personal'),\n createdBy: text('created_by').notNull().references(() => userTable.id, { onDelete: 'cascade' }),\n createdAt: createdAt(),\n updatedAt: updatedAt(),\n }, (table) => [\n index('idx_organization_created_by').on(table.createdBy),\n ])\n\n const organizationMembers = sqliteTable('organization_member', {\n id: hexId(),\n organizationId: text('organization_id').notNull().references(() => organizations.id, { onDelete: 'cascade' }),\n userId: text('user_id').notNull().references(() => userTable.id, { onDelete: 'cascade' }),\n role: text('role', { enum: ['owner', 'admin', 'member', 'billing'] }).notNull().default('member'),\n createdAt: createdAt(),\n updatedAt: updatedAt(),\n }, (table) => [\n uniqueIndex('uniq_org_member_user').on(table.organizationId, table.userId),\n index('idx_org_member_user').on(table.userId),\n ])\n\n const workspaceMembers = sqliteTable('workspace_member', {\n id: hexId(),\n workspaceId: text('workspace_id').notNull().references(() => workspaceTable.id, { onDelete: 'cascade' }),\n organizationMemberId: text('organization_member_id').references(() => organizationMembers.id, { onDelete: 'cascade' }),\n userId: text('user_id').references(() => userTable.id, { onDelete: 'cascade' }),\n role: text('role', { enum: ['owner', 'admin', 'editor', 'viewer'] }).notNull().default('editor'),\n invitedBy: text('invited_by'),\n inviteEmail: text('invite_email'),\n inviteToken: text('invite_token'),\n invitedAt: integer('invited_at', { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`),\n acceptedAt: integer('accepted_at', { mode: 'timestamp' }),\n }, (table) => [\n uniqueIndex('uniq_workspace_member_org_member').on(table.workspaceId, table.organizationMemberId),\n index('idx_workspace_member_user').on(table.workspaceId, table.userId),\n index('idx_member_user').on(table.userId),\n uniqueIndex('idx_member_invite_token').on(table.inviteToken),\n ])\n\n return { organizations, organizationMembers, workspaceMembers }\n}\n\nexport type TeamTables = ReturnType<typeof createTeamTables>\n\nexport type OrganizationRow = TeamTables['organizations']['$inferSelect']\nexport type OrganizationMemberRow = TeamTables['organizationMembers']['$inferSelect']\nexport type WorkspaceMemberRow = TeamTables['workspaceMembers']['$inferSelect']\n","/**\n * RBAC access builders over the teams tables. `createWorkspaceAccess` and\n * `createOrganizationAccess` close over the product's `db`, the tables from\n * `createTeamTables`, and the product's `workspace` table, returning the exact\n * `getWorkspaceAccess` / `requireWorkspaceAccess` / `listUserWorkspaces` /\n * `getOrganizationAccess` / `requireOrganizationAccess` functions a consumer\n * already calls — so adoption is a one-line import swap, every call site stays\n * identical.\n *\n * Defense in depth: org owners/admins are workspace owners across the org;\n * everyone else gets their explicit per-workspace role. Effective role is the\n * fold in `resolveWorkspaceRole` (pure, from `../roles`). Every query pins\n * `userId`, so a leaked id can never read across the tenancy boundary — it\n * surfaces as \"not found\".\n *\n * Driver-agnostic: builders are awaited, never `.run()`/`.all()`, so\n * better-sqlite3, D1, and libsql handles all behave identically.\n */\n\nimport { and, desc, eq, isNotNull } from 'drizzle-orm'\nimport type { BaseSQLiteDatabase } from 'drizzle-orm/sqlite-core'\nimport {\n type OrganizationRole,\n type WorkspaceRole,\n hasOrganizationRole,\n hasWorkspaceRole,\n resolveWorkspaceRole,\n} from '../roles'\nimport type { TeamParentTable } from './schema'\nimport type { OrganizationMemberRow, OrganizationRow, TeamTables } from './schema'\n\n/** Any SQLite drizzle database — `any` erases driver-specific generics so\n * better-sqlite3, D1, and libsql handles all fit. */\nexport type TeamDatabase = BaseSQLiteDatabase<'sync' | 'async', any, any>\n\n/**\n * The product's workspace table, narrowed to the columns the access joins read.\n * Adopters pass their real drizzle workspace table — it carries these columns\n * (gtm's does); the type only asserts the minimum the joins touch.\n */\nexport interface WorkspaceAccessTable {\n id: any\n organizationId: any\n name: any\n updatedAt: any\n}\n\nexport interface CreateAccessOptions {\n db: TeamDatabase\n tables: TeamTables\n /** The product's workspace table (the FK target passed to createTeamTables). */\n workspaceTable: TeamParentTable & WorkspaceAccessTable\n}\n\nexport interface WorkspaceAccess {\n workspace: Record<string, unknown>\n organization: OrganizationRow\n organizationMember: OrganizationMemberRow\n role: WorkspaceRole\n}\n\nexport interface UserWorkspaceSummary extends Record<string, unknown> {\n organizationId: string\n organizationName: string\n role: WorkspaceRole\n}\n\nexport interface WorkspaceAccessApi {\n getWorkspaceAccess(workspaceId: string, userId: string, minRole?: WorkspaceRole): Promise<WorkspaceAccess | null>\n requireWorkspaceAccess(workspaceId: string, userId: string, minRole?: WorkspaceRole): Promise<WorkspaceAccess>\n listUserWorkspaces(userId: string): Promise<UserWorkspaceSummary[]>\n}\n\nexport function createWorkspaceAccess(opts: CreateAccessOptions): WorkspaceAccessApi {\n const { db, tables, workspaceTable } = opts\n const { organizations, organizationMembers, workspaceMembers } = tables\n const workspaces = workspaceTable\n\n async function getWorkspaceAccess(\n workspaceId: string,\n userId: string,\n minRole: WorkspaceRole = 'viewer',\n ): Promise<WorkspaceAccess | null> {\n const [row] = await db\n .select({\n workspace: workspaces,\n organization: organizations,\n organizationMember: organizationMembers,\n projectRole: workspaceMembers.role,\n acceptedAt: workspaceMembers.acceptedAt,\n })\n .from(workspaces)\n .innerJoin(organizations, eq(organizations.id, workspaces.organizationId))\n .innerJoin(organizationMembers, and(\n eq(organizationMembers.organizationId, workspaces.organizationId),\n eq(organizationMembers.userId, userId),\n ))\n .leftJoin(workspaceMembers, and(\n eq(workspaceMembers.workspaceId, workspaces.id),\n eq(workspaceMembers.organizationMemberId, organizationMembers.id),\n isNotNull(workspaceMembers.acceptedAt),\n ))\n .where(eq(workspaces.id, workspaceId))\n .limit(1)\n\n if (!row) return null\n\n const role = resolveWorkspaceRole(row.organizationMember.role, row.projectRole as WorkspaceRole | null)\n if (!role) return null\n if (!hasWorkspaceRole(role, minRole)) return null\n\n return {\n workspace: row.workspace as Record<string, unknown>,\n organization: row.organization,\n organizationMember: row.organizationMember,\n role,\n }\n }\n\n async function requireWorkspaceAccess(\n workspaceId: string,\n userId: string,\n minRole: WorkspaceRole = 'viewer',\n ): Promise<WorkspaceAccess> {\n const access = await getWorkspaceAccess(workspaceId, userId, minRole)\n if (!access) throw new Response('Workspace not found', { status: 404 })\n return access\n }\n\n async function listUserWorkspaces(userId: string): Promise<UserWorkspaceSummary[]> {\n const rows = await db\n .select({\n workspace: workspaces,\n organization: organizations,\n organizationMember: organizationMembers,\n projectRole: workspaceMembers.role,\n })\n .from(workspaces)\n .innerJoin(organizations, eq(organizations.id, workspaces.organizationId))\n .innerJoin(organizationMembers, and(\n eq(organizationMembers.organizationId, workspaces.organizationId),\n eq(organizationMembers.userId, userId),\n ))\n .leftJoin(workspaceMembers, and(\n eq(workspaceMembers.workspaceId, workspaces.id),\n eq(workspaceMembers.organizationMemberId, organizationMembers.id),\n isNotNull(workspaceMembers.acceptedAt),\n ))\n // Most recently active workspace first, so callers taking rows[0] as the\n // default pick the freshest one.\n .orderBy(desc(workspaces.updatedAt))\n\n return rows.flatMap((row: any) => {\n const role = resolveWorkspaceRole(row.organizationMember.role, row.projectRole as WorkspaceRole | null)\n if (!role) return []\n return [{\n ...(row.workspace as Record<string, unknown>),\n organizationId: row.organization.id,\n organizationName: row.organization.name,\n role,\n } as UserWorkspaceSummary]\n })\n }\n\n return { getWorkspaceAccess, requireWorkspaceAccess, listUserWorkspaces }\n}\n\nexport interface OrganizationAccess {\n organization: OrganizationRow\n member: OrganizationMemberRow\n role: OrganizationRole\n}\n\nexport interface OrganizationAccessApi {\n getOrganizationAccess(organizationId: string, userId: string, minRole?: OrganizationRole): Promise<OrganizationAccess | null>\n requireOrganizationAccess(organizationId: string, userId: string, minRole?: OrganizationRole): Promise<OrganizationAccess>\n}\n\nexport interface CreateOrganizationAccessOptions {\n db: TeamDatabase\n tables: TeamTables\n}\n\nexport function createOrganizationAccess(opts: CreateOrganizationAccessOptions): OrganizationAccessApi {\n const { db, tables } = opts\n const { organizations, organizationMembers } = tables\n\n async function getOrganizationAccess(\n organizationId: string,\n userId: string,\n minRole: OrganizationRole = 'member',\n ): Promise<OrganizationAccess | null> {\n const [row] = await db\n .select({\n organization: organizations,\n member: organizationMembers,\n })\n .from(organizationMembers)\n .innerJoin(organizations, eq(organizations.id, organizationMembers.organizationId))\n .where(and(\n eq(organizationMembers.organizationId, organizationId),\n eq(organizationMembers.userId, userId),\n ))\n .limit(1)\n\n if (!row) return null\n const role = row.member.role as OrganizationRole\n if (!hasOrganizationRole(role, minRole)) return null\n return { organization: row.organization, member: row.member, role }\n }\n\n async function requireOrganizationAccess(\n organizationId: string,\n userId: string,\n minRole: OrganizationRole = 'member',\n ): Promise<OrganizationAccess> {\n const access = await getOrganizationAccess(organizationId, userId, minRole)\n if (!access) throw new Response('Organization not found', { status: 404 })\n return access\n }\n\n return { getOrganizationAccess, requireOrganizationAccess }\n}\n","/**\n * `ensurePersonalOrganization` — the substrate that makes solo-user adoption\n * work. Every user gets exactly one auto-created `kind: 'personal'` org with\n * the user as `owner` member. That org is the tenant that owns their\n * workspaces, so role resolution (`getWorkspaceAccess`) has an org row to read\n * even before any team/invite exists.\n *\n * Idempotent by construction: the org `slug` is derived from the user id\n * (`personal-<id>`) and upserted; the membership upserts on the\n * (organizationId, userId) unique index. Concurrent calls converge — no\n * duplicate personal orgs, no duplicate owner rows.\n *\n * ADOPTION CONTRACT: call this once per user at first authenticated entry\n * (e.g. right after sign-in / session bootstrap) BEFORE the first\n * `getWorkspaceAccess`. Role resolution requires the org membership to exist;\n * an adopter that skips this will see `null` access for a brand-new user with\n * no org row. There is no implicit creation inside the access builders — that\n * keeps reads side-effect-free; provisioning is this explicit call.\n */\n\nimport { and, eq } from 'drizzle-orm'\nimport type { OrganizationRole } from '../roles'\nimport type { TeamDatabase } from './access'\nimport type { OrganizationMemberRow, OrganizationRow, TeamTables } from './schema'\n\nexport interface EnsurePersonalOrganizationUser {\n id: string\n name?: string | null\n email?: string | null\n}\n\nexport interface PersonalOrganizationResult {\n organization: OrganizationRow\n member: OrganizationMemberRow\n role: OrganizationRole\n}\n\nexport interface CreatePersonalOrganizationOptions {\n db: TeamDatabase\n tables: TeamTables\n}\n\nexport function createEnsurePersonalOrganization(opts: CreatePersonalOrganizationOptions) {\n const { db, tables } = opts\n const { organizations, organizationMembers } = tables\n\n return async function ensurePersonalOrganization(\n user: EnsurePersonalOrganizationUser,\n ): Promise<PersonalOrganizationResult> {\n const [existing] = await db\n .select({\n organization: organizations,\n member: organizationMembers,\n })\n .from(organizationMembers)\n .innerJoin(organizations, eq(organizations.id, organizationMembers.organizationId))\n .where(and(\n eq(organizationMembers.userId, user.id),\n eq(organizations.kind, 'personal'),\n ))\n .limit(1)\n\n if (existing) {\n return {\n organization: existing.organization,\n member: existing.member,\n role: existing.member.role as OrganizationRole,\n }\n }\n\n const orgName = user.name?.trim() || user.email?.split('@')[0] || 'Personal'\n const slug = `personal-${user.id}`\n const [organization] = await db\n .insert(organizations)\n .values({\n name: `${orgName}'s Organization`,\n slug,\n kind: 'personal',\n createdBy: user.id,\n })\n .onConflictDoUpdate({\n target: organizations.slug,\n set: { updatedAt: new Date() },\n })\n .returning()\n\n // A single-row upsert with .returning() always yields exactly one row.\n const org = organization as OrganizationRow\n\n const [member] = await db\n .insert(organizationMembers)\n .values({\n organizationId: org.id,\n userId: user.id,\n role: 'owner',\n })\n .onConflictDoUpdate({\n target: [organizationMembers.organizationId, organizationMembers.userId],\n set: { role: 'owner', updatedAt: new Date() },\n })\n .returning()\n\n return { organization: org, member: member as OrganizationMemberRow, role: 'owner' }\n }\n}\n"],"mappings":"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","names":["and","eq"]}
@@ -0,0 +1,44 @@
1
+ export { A as ASSIGNABLE_WORKSPACE_ROLES, a as AssignableWorkspaceRole, b as ORGANIZATION_ROLES, c as ORGANIZATION_ROLE_RANK, O as OrganizationRole, S as SandboxWorkspaceRole, W as WORKSPACE_ROLES, d as WORKSPACE_ROLE_RANK, e as WorkspaceCollaborationAccess, f as WorkspaceRole, g as canManageWorkspaceMemberRole, h as hasOrganizationRole, i as hasWorkspaceRole, j as isAssignableWorkspaceRole, o as organizationRoleGrantsWorkspaceOwner, r as resolveWorkspaceRole, w as workspaceRoleToCollaborationAccess, k as workspaceRoleToSandboxRole } from '../roles-CLtYKHHl.js';
2
+
3
+ /**
4
+ * Pure invite-token helpers — generation, shape validation, and expiry math.
5
+ * No I/O: the members API persists/looks up tokens; these functions only
6
+ * produce well-formed tokens and decide, given values the caller already
7
+ * loaded, whether an invite is usable.
8
+ *
9
+ * A token is an opaque high-entropy URL-safe string. It is the bearer secret
10
+ * in `/invite/:token`, so it must be unguessable and never derived from the
11
+ * email or workspace. `generateInviteToken` uses Web Crypto (`crypto`), which
12
+ * is present in Workers, Node 18+, Deno, and browsers — no Node-only import,
13
+ * so this stays a pure leaf.
14
+ */
15
+ /** Cryptographically-random, URL-safe (base64url) invite token. */
16
+ declare function generateInviteToken(): string;
17
+ /** True when `value` has the shape of an invite token (not whether it exists). */
18
+ declare function isInviteTokenShape(value: unknown): value is string;
19
+ /** A pending invite row, narrowed to the fields invite acceptance reasons over. */
20
+ interface InviteTokenState {
21
+ /** null until accepted — a non-null value means the invite was already used. */
22
+ acceptedAt: Date | number | null | undefined;
23
+ /** Email the invite was addressed to, if any. */
24
+ inviteEmail?: string | null;
25
+ /** Optional hard expiry; omit/undefined for invites that never expire. */
26
+ expiresAt?: Date | number | null;
27
+ }
28
+ type InviteRejectionReason = 'already-accepted' | 'expired' | 'email-mismatch';
29
+ interface InviteValidationResult {
30
+ ok: boolean;
31
+ reason?: InviteRejectionReason;
32
+ }
33
+ /**
34
+ * Decide whether a loaded invite can be accepted by `acceptingEmail` at `now`.
35
+ * Pure: the caller has already fetched the row by token; this only judges it.
36
+ * Email match is case-insensitive and only enforced when the invite was
37
+ * addressed to a specific email (an open invite has no `inviteEmail`).
38
+ */
39
+ declare function validateInviteToken(invite: InviteTokenState, opts?: {
40
+ acceptingEmail?: string | null;
41
+ now?: Date;
42
+ }): InviteValidationResult;
43
+
44
+ export { type InviteRejectionReason, type InviteTokenState, type InviteValidationResult, generateInviteToken, isInviteTokenShape, validateInviteToken };
@@ -0,0 +1,39 @@
1
+ import {
2
+ generateInviteToken,
3
+ isInviteTokenShape,
4
+ validateInviteToken
5
+ } from "../chunk-3SVAA3MA.js";
6
+ import {
7
+ ASSIGNABLE_WORKSPACE_ROLES,
8
+ ORGANIZATION_ROLES,
9
+ ORGANIZATION_ROLE_RANK,
10
+ WORKSPACE_ROLES,
11
+ WORKSPACE_ROLE_RANK,
12
+ canManageWorkspaceMemberRole,
13
+ hasOrganizationRole,
14
+ hasWorkspaceRole,
15
+ isAssignableWorkspaceRole,
16
+ organizationRoleGrantsWorkspaceOwner,
17
+ resolveWorkspaceRole,
18
+ workspaceRoleToCollaborationAccess,
19
+ workspaceRoleToSandboxRole
20
+ } from "../chunk-63CE7FEZ.js";
21
+ export {
22
+ ASSIGNABLE_WORKSPACE_ROLES,
23
+ ORGANIZATION_ROLES,
24
+ ORGANIZATION_ROLE_RANK,
25
+ WORKSPACE_ROLES,
26
+ WORKSPACE_ROLE_RANK,
27
+ canManageWorkspaceMemberRole,
28
+ generateInviteToken,
29
+ hasOrganizationRole,
30
+ hasWorkspaceRole,
31
+ isAssignableWorkspaceRole,
32
+ isInviteTokenShape,
33
+ organizationRoleGrantsWorkspaceOwner,
34
+ resolveWorkspaceRole,
35
+ validateInviteToken,
36
+ workspaceRoleToCollaborationAccess,
37
+ workspaceRoleToSandboxRole
38
+ };
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}