@shipfox/api-workspaces 11.0.0 → 12.2.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 (140) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +27 -4
  4. package/dist/core/entities/workspace.d.ts +1 -0
  5. package/dist/core/entities/workspace.d.ts.map +1 -1
  6. package/dist/core/entities/workspace.js.map +1 -1
  7. package/dist/core/errors.d.ts +3 -0
  8. package/dist/core/errors.d.ts.map +1 -1
  9. package/dist/core/errors.js +6 -0
  10. package/dist/core/errors.js.map +1 -1
  11. package/dist/core/index.d.ts +2 -2
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/core/index.js +2 -2
  14. package/dist/core/index.js.map +1 -1
  15. package/dist/core/invitations.js +1 -1
  16. package/dist/core/invitations.js.map +1 -1
  17. package/dist/core/rate-limit.d.ts +39 -0
  18. package/dist/core/rate-limit.d.ts.map +1 -0
  19. package/dist/core/rate-limit.js +70 -0
  20. package/dist/core/rate-limit.js.map +1 -0
  21. package/dist/core/workspaces.d.ts +7 -0
  22. package/dist/core/workspaces.d.ts.map +1 -1
  23. package/dist/core/workspaces.js +42 -4
  24. package/dist/core/workspaces.js.map +1 -1
  25. package/dist/db/db.d.ts +332 -12
  26. package/dist/db/db.d.ts.map +1 -1
  27. package/dist/db/db.js +3 -1
  28. package/dist/db/db.js.map +1 -1
  29. package/dist/db/index.d.ts +4 -1
  30. package/dist/db/index.d.ts.map +1 -1
  31. package/dist/db/index.js +3 -1
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/db/memberships.d.ts +1 -0
  34. package/dist/db/memberships.d.ts.map +1 -1
  35. package/dist/db/memberships.js +2 -0
  36. package/dist/db/memberships.js.map +1 -1
  37. package/dist/db/rate-limits.d.ts +9 -0
  38. package/dist/db/rate-limits.d.ts.map +1 -0
  39. package/dist/db/rate-limits.js +43 -0
  40. package/dist/db/rate-limits.js.map +1 -0
  41. package/dist/db/schema/admin-command-results.d.ts +1 -1
  42. package/dist/db/schema/invitations.d.ts +1 -1
  43. package/dist/db/schema/memberships.d.ts +1 -1
  44. package/dist/db/schema/outbox.d.ts +1 -1
  45. package/dist/db/schema/rate-limits.d.ts +145 -0
  46. package/dist/db/schema/rate-limits.d.ts.map +1 -0
  47. package/dist/db/schema/rate-limits.js +25 -0
  48. package/dist/db/schema/rate-limits.js.map +1 -0
  49. package/dist/db/schema/workspaces.d.ts +19 -2
  50. package/dist/db/schema/workspaces.d.ts.map +1 -1
  51. package/dist/db/schema/workspaces.js +5 -2
  52. package/dist/db/schema/workspaces.js.map +1 -1
  53. package/dist/db/workspaces.d.ts +11 -1
  54. package/dist/db/workspaces.d.ts.map +1 -1
  55. package/dist/db/workspaces.js +12 -3
  56. package/dist/db/workspaces.js.map +1 -1
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/metrics/instance.d.ts +6 -0
  62. package/dist/metrics/instance.d.ts.map +1 -1
  63. package/dist/metrics/instance.js +12 -0
  64. package/dist/metrics/instance.js.map +1 -1
  65. package/dist/presentation/dto/membership.d.ts.map +1 -1
  66. package/dist/presentation/dto/membership.js +1 -0
  67. package/dist/presentation/dto/membership.js.map +1 -1
  68. package/dist/presentation/dto/workspace.d.ts.map +1 -1
  69. package/dist/presentation/dto/workspace.js +1 -0
  70. package/dist/presentation/dto/workspace.js.map +1 -1
  71. package/dist/presentation/e2eRoutes/create-workspace.d.ts.map +1 -1
  72. package/dist/presentation/e2eRoutes/create-workspace.js +11 -1
  73. package/dist/presentation/e2eRoutes/create-workspace.js.map +1 -1
  74. package/dist/presentation/routes/index.d.ts.map +1 -1
  75. package/dist/presentation/routes/index.js +3 -1
  76. package/dist/presentation/routes/index.js.map +1 -1
  77. package/dist/presentation/routes/rate-limit.d.ts +3 -0
  78. package/dist/presentation/routes/rate-limit.d.ts.map +1 -0
  79. package/dist/presentation/routes/rate-limit.js +42 -0
  80. package/dist/presentation/routes/rate-limit.js.map +1 -0
  81. package/dist/presentation/routes/workspaces/create.d.ts.map +1 -1
  82. package/dist/presentation/routes/workspaces/create.js +11 -1
  83. package/dist/presentation/routes/workspaces/create.js.map +1 -1
  84. package/dist/presentation/routes/workspaces/index.d.ts +2 -0
  85. package/dist/presentation/routes/workspaces/index.d.ts.map +1 -1
  86. package/dist/presentation/routes/workspaces/index.js +2 -0
  87. package/dist/presentation/routes/workspaces/index.js.map +1 -1
  88. package/dist/presentation/routes/workspaces/slug-availability.d.ts +2 -0
  89. package/dist/presentation/routes/workspaces/slug-availability.d.ts.map +1 -0
  90. package/dist/presentation/routes/workspaces/slug-availability.js +31 -0
  91. package/dist/presentation/routes/workspaces/slug-availability.js.map +1 -0
  92. package/dist/presentation/routes/workspaces/update.d.ts +2 -0
  93. package/dist/presentation/routes/workspaces/update.d.ts.map +1 -0
  94. package/dist/presentation/routes/workspaces/update.js +49 -0
  95. package/dist/presentation/routes/workspaces/update.js.map +1 -0
  96. package/dist/tsconfig.test.tsbuildinfo +1 -1
  97. package/drizzle/0000_initial.sql +2 -0
  98. package/drizzle/0003_dapper_talos.sql +13 -0
  99. package/drizzle/meta/0000_snapshot.json +23 -1
  100. package/drizzle/meta/0003_snapshot.json +689 -0
  101. package/drizzle/meta/_journal.json +7 -0
  102. package/package.json +16 -14
  103. package/src/core/entities/workspace.ts +1 -0
  104. package/src/core/errors.ts +7 -0
  105. package/src/core/index.ts +3 -0
  106. package/src/core/invitations.ts +1 -1
  107. package/src/core/rate-limit.test.ts +113 -0
  108. package/src/core/rate-limit.ts +119 -0
  109. package/src/core/workspaces.test.ts +9 -0
  110. package/src/core/workspaces.ts +47 -3
  111. package/src/db/db.ts +2 -0
  112. package/src/db/index.ts +10 -0
  113. package/src/db/memberships.ts +3 -0
  114. package/src/db/rate-limits.test.ts +74 -0
  115. package/src/db/rate-limits.ts +59 -0
  116. package/src/db/schema/rate-limits.ts +27 -0
  117. package/src/db/schema/workspaces.ts +7 -2
  118. package/src/db/workspaces.ts +21 -1
  119. package/src/index.test.ts +2 -0
  120. package/src/index.ts +1 -0
  121. package/src/metrics/instance.ts +26 -0
  122. package/src/presentation/dto/membership.ts +1 -0
  123. package/src/presentation/dto/workspace.ts +1 -0
  124. package/src/presentation/e2eRoutes/create-workspace.ts +9 -1
  125. package/src/presentation/inter-module.test.ts +22 -4
  126. package/src/presentation/routes/index.ts +8 -1
  127. package/src/presentation/routes/rate-limit.ts +45 -0
  128. package/src/presentation/routes/workspaces/create.test.ts +33 -3
  129. package/src/presentation/routes/workspaces/create.ts +9 -1
  130. package/src/presentation/routes/workspaces/index.ts +2 -0
  131. package/src/presentation/routes/workspaces/list.test.ts +1 -0
  132. package/src/presentation/routes/workspaces/slug-availability.test.ts +159 -0
  133. package/src/presentation/routes/workspaces/slug-availability.ts +30 -0
  134. package/src/presentation/routes/workspaces/update.test.ts +114 -0
  135. package/src/presentation/routes/workspaces/update.ts +41 -0
  136. package/test/env.ts +1 -0
  137. package/test/factories/workspace.ts +3 -1
  138. package/test/globalSetup.ts +1 -1
  139. package/test/routes.ts +18 -4
  140. package/tsconfig.build.tsbuildinfo +1 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport {administrationActionEventSchemas} from '@shipfox/api-common-dto';\nimport type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {\n WORKSPACES_INVITATION_SEND_REQUESTED,\n type WorkspacesEventMap,\n workspacesEventSchemas,\n} from '@shipfox/api-workspaces-dto';\nimport type {ShipfoxModule} from '@shipfox/node-module';\nimport {subscriberFactory} from '@shipfox/node-module';\nimport {db, migrationsPath, workspacesOutbox} from '#db/index.js';\nimport {registerWorkspacesServiceMetrics} from '#metrics/index.js';\nimport {workspacesE2eRoutes} from '#presentation/e2eRoutes/index.js';\nimport {createWorkspacesInterModulePresentation} from '#presentation/inter-module.js';\nimport {createWorkspacesRoutes, workspacesRoutes} from '#presentation/routes/index.js';\nimport {onInvitationSendRequested} from '#presentation/subscribers/index.js';\n\nexport type {Invitation} from '#core/entities/invitation.js';\nexport type {Membership} from '#core/entities/membership.js';\nexport type {Workspace, WorkspaceStatus} from '#core/entities/workspace.js';\nexport {\n InvitationEmailMismatchError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n WorkspaceNotFoundError,\n} from '#core/errors.js';\nexport {\n acceptWorkspaceInvitation,\n peekInvitationByRawToken,\n reconcileWorkspaceInvitationAcceptance,\n type WorkspaceInvitationReconciliation,\n} from '#core/invitations.js';\nexport {type EnsureMembershipParams, ensureMembership} from '#core/memberships.js';\nexport {getWorkspace, requireWorkspaceMembership} from '#core/workspaces.js';\nexport {db, migrationsPath} from '#db/index.js';\nexport {listMembershipsByUser} from '#db/memberships.js';\nexport {createWorkspacesRoutes, workspacesRoutes as routes} from '#presentation/routes/index.js';\n\nconst subscriber = subscriberFactory<WorkspacesEventMap>();\nconst workspacesPublisherEventSchemas = {\n ...workspacesEventSchemas,\n ...administrationActionEventSchemas,\n};\n\nexport interface WorkspacesModuleOptions {\n auth: AuthInterModuleClient;\n projects: ProjectsModuleClient;\n runners: RunnersInterModuleClient;\n}\n\nexport function createWorkspacesModule(options: WorkspacesModuleOptions): ShipfoxModule {\n return {\n name: 'workspaces',\n database: {db, migrationsPath, databaseNamespace: 'workspaces'},\n routes: createWorkspacesRoutes(options),\n e2eRoutes: [workspacesE2eRoutes],\n publishers: [\n {\n name: 'workspaces',\n table: workspacesOutbox,\n db,\n eventSchemas: workspacesPublisherEventSchemas,\n },\n ],\n subscribers: [subscriber(WORKSPACES_INVITATION_SEND_REQUESTED, onInvitationSendRequested)],\n metrics: registerWorkspacesServiceMetrics,\n interModulePresentations: [createWorkspacesInterModulePresentation()],\n };\n}\n\n/** @deprecated Use createWorkspacesModule from the application composition root. */\nexport const workspacesModule: ShipfoxModule = {\n name: 'workspaces',\n database: {db, migrationsPath, databaseNamespace: 'workspaces'},\n routes: workspacesRoutes,\n e2eRoutes: [workspacesE2eRoutes],\n publishers: [\n {\n name: 'workspaces',\n table: workspacesOutbox,\n db,\n eventSchemas: workspacesPublisherEventSchemas,\n },\n ],\n subscribers: [subscriber(WORKSPACES_INVITATION_SEND_REQUESTED, onInvitationSendRequested)],\n metrics: registerWorkspacesServiceMetrics,\n interModulePresentations: [createWorkspacesInterModulePresentation()],\n};\n"],"names":["administrationActionEventSchemas","WORKSPACES_INVITATION_SEND_REQUESTED","workspacesEventSchemas","subscriberFactory","db","migrationsPath","workspacesOutbox","registerWorkspacesServiceMetrics","workspacesE2eRoutes","createWorkspacesInterModulePresentation","createWorkspacesRoutes","workspacesRoutes","onInvitationSendRequested","InvitationEmailMismatchError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","WorkspaceNotFoundError","acceptWorkspaceInvitation","peekInvitationByRawToken","reconcileWorkspaceInvitationAcceptance","ensureMembership","getWorkspace","requireWorkspaceMembership","listMembershipsByUser","routes","subscriber","workspacesPublisherEventSchemas","createWorkspacesModule","options","name","database","databaseNamespace","e2eRoutes","publishers","table","eventSchemas","subscribers","metrics","interModulePresentations","workspacesModule"],"mappings":"AACA,SAAQA,gCAAgC,QAAO,0BAA0B;AAGzE,SACEC,oCAAoC,EAEpCC,sBAAsB,QACjB,8BAA8B;AAErC,SAAQC,iBAAiB,QAAO,uBAAuB;AACvD,SAAQC,EAAE,EAAEC,cAAc,EAAEC,gBAAgB,QAAO,eAAe;AAClE,SAAQC,gCAAgC,QAAO,oBAAoB;AACnE,SAAQC,mBAAmB,QAAO,mCAAmC;AACrE,SAAQC,uCAAuC,QAAO,gCAAgC;AACtF,SAAQC,sBAAsB,EAAEC,gBAAgB,QAAO,gCAAgC;AACvF,SAAQC,yBAAyB,QAAO,qCAAqC;AAK7E,SACEC,4BAA4B,EAC5BC,qBAAqB,EACrBC,iBAAiB,EACjBC,iBAAiB,EACjBC,sBAAsB,QACjB,kBAAkB;AACzB,SACEC,yBAAyB,EACzBC,wBAAwB,EACxBC,sCAAsC,QAEjC,uBAAuB;AAC9B,SAAqCC,gBAAgB,QAAO,uBAAuB;AACnF,SAAQC,YAAY,EAAEC,0BAA0B,QAAO,sBAAsB;AAC7E,SAAQnB,EAAE,EAAEC,cAAc,QAAO,eAAe;AAChD,SAAQmB,qBAAqB,QAAO,qBAAqB;AACzD,SAAQd,sBAAsB,EAAEC,oBAAoBc,MAAM,QAAO,gCAAgC;AAEjG,MAAMC,aAAavB;AACnB,MAAMwB,kCAAkC;IACtC,GAAGzB,sBAAsB;IACzB,GAAGF,gCAAgC;AACrC;AAQA,OAAO,SAAS4B,uBAAuBC,OAAgC;IACrE,OAAO;QACLC,MAAM;QACNC,UAAU;YAAC3B;YAAIC;YAAgB2B,mBAAmB;QAAY;QAC9DP,QAAQf,uBAAuBmB;QAC/BI,WAAW;YAACzB;SAAoB;QAChC0B,YAAY;YACV;gBACEJ,MAAM;gBACNK,OAAO7B;gBACPF;gBACAgC,cAAcT;YAChB;SACD;QACDU,aAAa;YAACX,WAAWzB,sCAAsCW;SAA2B;QAC1F0B,SAAS/B;QACTgC,0BAA0B;YAAC9B;SAA0C;IACvE;AACF;AAEA,kFAAkF,GAClF,OAAO,MAAM+B,mBAAkC;IAC7CV,MAAM;IACNC,UAAU;QAAC3B;QAAIC;QAAgB2B,mBAAmB;IAAY;IAC9DP,QAAQd;IACRsB,WAAW;QAACzB;KAAoB;IAChC0B,YAAY;QACV;YACEJ,MAAM;YACNK,OAAO7B;YACPF;YACAgC,cAAcT;QAChB;KACD;IACDU,aAAa;QAACX,WAAWzB,sCAAsCW;KAA2B;IAC1F0B,SAAS/B;IACTgC,0BAA0B;QAAC9B;KAA0C;AACvE,EAAE"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport {administrationActionEventSchemas} from '@shipfox/api-common-dto';\nimport type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport {\n WORKSPACES_INVITATION_SEND_REQUESTED,\n type WorkspacesEventMap,\n workspacesEventSchemas,\n} from '@shipfox/api-workspaces-dto';\nimport type {ShipfoxModule} from '@shipfox/node-module';\nimport {subscriberFactory} from '@shipfox/node-module';\nimport {db, migrationsPath, workspacesOutbox} from '#db/index.js';\nimport {registerWorkspacesServiceMetrics} from '#metrics/index.js';\nimport {workspacesE2eRoutes} from '#presentation/e2eRoutes/index.js';\nimport {createWorkspacesInterModulePresentation} from '#presentation/inter-module.js';\nimport {createWorkspacesRoutes, workspacesRoutes} from '#presentation/routes/index.js';\nimport {onInvitationSendRequested} from '#presentation/subscribers/index.js';\n\nexport type {Invitation} from '#core/entities/invitation.js';\nexport type {Membership} from '#core/entities/membership.js';\nexport type {Workspace, WorkspaceStatus} from '#core/entities/workspace.js';\nexport {\n InvitationEmailMismatchError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n WorkspaceNotFoundError,\n WorkspaceSlugConflictError,\n} from '#core/errors.js';\nexport {\n acceptWorkspaceInvitation,\n peekInvitationByRawToken,\n reconcileWorkspaceInvitationAcceptance,\n type WorkspaceInvitationReconciliation,\n} from '#core/invitations.js';\nexport {type EnsureMembershipParams, ensureMembership} from '#core/memberships.js';\nexport {getWorkspace, requireWorkspaceMembership} from '#core/workspaces.js';\nexport {db, migrationsPath} from '#db/index.js';\nexport {listMembershipsByUser} from '#db/memberships.js';\nexport {createWorkspacesRoutes, workspacesRoutes as routes} from '#presentation/routes/index.js';\n\nconst subscriber = subscriberFactory<WorkspacesEventMap>();\nconst workspacesPublisherEventSchemas = {\n ...workspacesEventSchemas,\n ...administrationActionEventSchemas,\n};\n\nexport interface WorkspacesModuleOptions {\n auth: AuthInterModuleClient;\n projects: ProjectsModuleClient;\n runners: RunnersInterModuleClient;\n}\n\nexport function createWorkspacesModule(options: WorkspacesModuleOptions): ShipfoxModule {\n return {\n name: 'workspaces',\n database: {db, migrationsPath, databaseNamespace: 'workspaces'},\n routes: createWorkspacesRoutes(options),\n e2eRoutes: [workspacesE2eRoutes],\n publishers: [\n {\n name: 'workspaces',\n table: workspacesOutbox,\n db,\n eventSchemas: workspacesPublisherEventSchemas,\n },\n ],\n subscribers: [subscriber(WORKSPACES_INVITATION_SEND_REQUESTED, onInvitationSendRequested)],\n metrics: registerWorkspacesServiceMetrics,\n interModulePresentations: [createWorkspacesInterModulePresentation()],\n };\n}\n\n/** @deprecated Use createWorkspacesModule from the application composition root. */\nexport const workspacesModule: ShipfoxModule = {\n name: 'workspaces',\n database: {db, migrationsPath, databaseNamespace: 'workspaces'},\n routes: workspacesRoutes,\n e2eRoutes: [workspacesE2eRoutes],\n publishers: [\n {\n name: 'workspaces',\n table: workspacesOutbox,\n db,\n eventSchemas: workspacesPublisherEventSchemas,\n },\n ],\n subscribers: [subscriber(WORKSPACES_INVITATION_SEND_REQUESTED, onInvitationSendRequested)],\n metrics: registerWorkspacesServiceMetrics,\n interModulePresentations: [createWorkspacesInterModulePresentation()],\n};\n"],"names":["administrationActionEventSchemas","WORKSPACES_INVITATION_SEND_REQUESTED","workspacesEventSchemas","subscriberFactory","db","migrationsPath","workspacesOutbox","registerWorkspacesServiceMetrics","workspacesE2eRoutes","createWorkspacesInterModulePresentation","createWorkspacesRoutes","workspacesRoutes","onInvitationSendRequested","InvitationEmailMismatchError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","WorkspaceNotFoundError","WorkspaceSlugConflictError","acceptWorkspaceInvitation","peekInvitationByRawToken","reconcileWorkspaceInvitationAcceptance","ensureMembership","getWorkspace","requireWorkspaceMembership","listMembershipsByUser","routes","subscriber","workspacesPublisherEventSchemas","createWorkspacesModule","options","name","database","databaseNamespace","e2eRoutes","publishers","table","eventSchemas","subscribers","metrics","interModulePresentations","workspacesModule"],"mappings":"AACA,SAAQA,gCAAgC,QAAO,0BAA0B;AAGzE,SACEC,oCAAoC,EAEpCC,sBAAsB,QACjB,8BAA8B;AAErC,SAAQC,iBAAiB,QAAO,uBAAuB;AACvD,SAAQC,EAAE,EAAEC,cAAc,EAAEC,gBAAgB,QAAO,eAAe;AAClE,SAAQC,gCAAgC,QAAO,oBAAoB;AACnE,SAAQC,mBAAmB,QAAO,mCAAmC;AACrE,SAAQC,uCAAuC,QAAO,gCAAgC;AACtF,SAAQC,sBAAsB,EAAEC,gBAAgB,QAAO,gCAAgC;AACvF,SAAQC,yBAAyB,QAAO,qCAAqC;AAK7E,SACEC,4BAA4B,EAC5BC,qBAAqB,EACrBC,iBAAiB,EACjBC,iBAAiB,EACjBC,sBAAsB,EACtBC,0BAA0B,QACrB,kBAAkB;AACzB,SACEC,yBAAyB,EACzBC,wBAAwB,EACxBC,sCAAsC,QAEjC,uBAAuB;AAC9B,SAAqCC,gBAAgB,QAAO,uBAAuB;AACnF,SAAQC,YAAY,EAAEC,0BAA0B,QAAO,sBAAsB;AAC7E,SAAQpB,EAAE,EAAEC,cAAc,QAAO,eAAe;AAChD,SAAQoB,qBAAqB,QAAO,qBAAqB;AACzD,SAAQf,sBAAsB,EAAEC,oBAAoBe,MAAM,QAAO,gCAAgC;AAEjG,MAAMC,aAAaxB;AACnB,MAAMyB,kCAAkC;IACtC,GAAG1B,sBAAsB;IACzB,GAAGF,gCAAgC;AACrC;AAQA,OAAO,SAAS6B,uBAAuBC,OAAgC;IACrE,OAAO;QACLC,MAAM;QACNC,UAAU;YAAC5B;YAAIC;YAAgB4B,mBAAmB;QAAY;QAC9DP,QAAQhB,uBAAuBoB;QAC/BI,WAAW;YAAC1B;SAAoB;QAChC2B,YAAY;YACV;gBACEJ,MAAM;gBACNK,OAAO9B;gBACPF;gBACAiC,cAAcT;YAChB;SACD;QACDU,aAAa;YAACX,WAAW1B,sCAAsCW;SAA2B;QAC1F2B,SAAShC;QACTiC,0BAA0B;YAAC/B;SAA0C;IACvE;AACF;AAEA,kFAAkF,GAClF,OAAO,MAAMgC,mBAAkC;IAC7CV,MAAM;IACNC,UAAU;QAAC5B;QAAIC;QAAgB4B,mBAAmB;IAAY;IAC9DP,QAAQf;IACRuB,WAAW;QAAC1B;KAAoB;IAChC2B,YAAY;QACV;YACEJ,MAAM;YACNK,OAAO9B;YACPF;YACAiC,cAAcT;QAChB;KACD;IACDU,aAAa;QAACX,WAAW1B,sCAAsCW;KAA2B;IAC1F2B,SAAShC;IACTiC,0BAA0B;QAAC/B;KAA0C;AACvE,EAAE"}
@@ -1,8 +1,14 @@
1
1
  export type WorkspacesInvitationEmailRequested = 'requested' | 'skipped';
2
2
  export type WorkspacesMembershipChangeAction = 'added' | 'removed';
3
3
  export type WorkspacesInvitationAcceptOutcome = 'added' | 'already_member';
4
+ export type WorkspacesRateLimitOutcome = 'allowed' | 'blocked' | 'unavailable';
4
5
  export declare function recordWorkspaceCreated(): void;
5
6
  export declare function recordWorkspaceMembershipChanged(action: WorkspacesMembershipChangeAction): void;
6
7
  export declare function recordWorkspaceInvitationCreated(emailRequested: WorkspacesInvitationEmailRequested): void;
7
8
  export declare function recordWorkspaceInvitationAccepted(outcome: WorkspacesInvitationAcceptOutcome): void;
9
+ export declare function recordWorkspaceRateLimitCheck(params: {
10
+ action: 'slug-availability';
11
+ outcome: WorkspacesRateLimitOutcome;
12
+ }): void;
13
+ export declare function recordWorkspaceRateLimitPruneFailure(): void;
8
14
  //# sourceMappingURL=instance.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/metrics/instance.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kCAAkC,GAAG,WAAW,GAAG,SAAS,CAAC;AACzE,MAAM,MAAM,gCAAgC,GAAG,OAAO,GAAG,SAAS,CAAC;AACnE,MAAM,MAAM,iCAAiC,GAAG,OAAO,GAAG,gBAAgB,CAAC;AAkC3E,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,gCAAgC,GAAG,IAAI,CAE/F;AAED,wBAAgB,gCAAgC,CAC9C,cAAc,EAAE,kCAAkC,GACjD,IAAI,CAEN;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,iCAAiC,GACzC,IAAI,CAEN"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/metrics/instance.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kCAAkC,GAAG,WAAW,GAAG,SAAS,CAAC;AACzE,MAAM,MAAM,gCAAgC,GAAG,OAAO,GAAG,SAAS,CAAC;AACnE,MAAM,MAAM,iCAAiC,GAAG,OAAO,GAAG,gBAAgB,CAAC;AAC3E,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAgD/E,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,gCAAgC,GAAG,IAAI,CAE/F;AAED,wBAAgB,gCAAgC,CAC9C,cAAc,EAAE,kCAAkC,GACjD,IAAI,CAEN;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,iCAAiC,GACzC,IAAI,CAEN;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,0BAA0B,CAAC;CACrC,GAAG,IAAI,CAEP;AAED,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D"}
@@ -12,6 +12,12 @@ const invitationCreatedCount = meter.createCounter('workspaces_invitation_create
12
12
  const invitationAcceptedCount = meter.createCounter('workspaces_invitation_accepted', {
13
13
  description: 'Workspace invitations accepted by membership outcome'
14
14
  });
15
+ const workspaceRateLimitCheckCount = meter.createCounter('workspaces_rate_limit_checks', {
16
+ description: 'Workspace rate-limit checks by action and outcome'
17
+ });
18
+ const workspaceRateLimitPruneFailureCount = meter.createCounter('workspaces_rate_limit_prune_failures', {
19
+ description: 'Workspace rate-limit prune failures'
20
+ });
15
21
  function recordMetric(record) {
16
22
  try {
17
23
  record();
@@ -37,5 +43,11 @@ export function recordWorkspaceInvitationAccepted(outcome) {
37
43
  outcome
38
44
  }));
39
45
  }
46
+ export function recordWorkspaceRateLimitCheck(params) {
47
+ recordMetric(()=>workspaceRateLimitCheckCount.add(1, params));
48
+ }
49
+ export function recordWorkspaceRateLimitPruneFailure() {
50
+ recordMetric(()=>workspaceRateLimitPruneFailureCount.add(1));
51
+ }
40
52
 
41
53
  //# sourceMappingURL=instance.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/metrics/instance.ts"],"sourcesContent":["import {instanceMetrics} from '@shipfox/node-opentelemetry';\n\nexport type WorkspacesInvitationEmailRequested = 'requested' | 'skipped';\nexport type WorkspacesMembershipChangeAction = 'added' | 'removed';\nexport type WorkspacesInvitationAcceptOutcome = 'added' | 'already_member';\n\nconst meter = instanceMetrics.getMeter('workspaces');\n\nconst workspaceCreatedCount = meter.createCounter<Record<string, never>>('workspaces_created', {\n description: 'Workspaces created',\n});\n\nconst membershipChangedCount = meter.createCounter<{\n action: WorkspacesMembershipChangeAction;\n}>('workspaces_membership_changed', {\n description: 'Workspace membership changes by action',\n});\n\nconst invitationCreatedCount = meter.createCounter<{\n email_requested: WorkspacesInvitationEmailRequested;\n}>('workspaces_invitation_created', {\n description: 'Workspace invitations created by whether an invitation email was requested',\n});\n\nconst invitationAcceptedCount = meter.createCounter<{\n outcome: WorkspacesInvitationAcceptOutcome;\n}>('workspaces_invitation_accepted', {\n description: 'Workspace invitations accepted by membership outcome',\n});\n\nfunction recordMetric(record: () => void): void {\n try {\n record();\n } catch {\n // Metrics must not affect workspace mutations.\n }\n}\n\nexport function recordWorkspaceCreated(): void {\n recordMetric(() => workspaceCreatedCount.add(1));\n}\n\nexport function recordWorkspaceMembershipChanged(action: WorkspacesMembershipChangeAction): void {\n recordMetric(() => membershipChangedCount.add(1, {action}));\n}\n\nexport function recordWorkspaceInvitationCreated(\n emailRequested: WorkspacesInvitationEmailRequested,\n): void {\n recordMetric(() => invitationCreatedCount.add(1, {email_requested: emailRequested}));\n}\n\nexport function recordWorkspaceInvitationAccepted(\n outcome: WorkspacesInvitationAcceptOutcome,\n): void {\n recordMetric(() => invitationAcceptedCount.add(1, {outcome}));\n}\n"],"names":["instanceMetrics","meter","getMeter","workspaceCreatedCount","createCounter","description","membershipChangedCount","invitationCreatedCount","invitationAcceptedCount","recordMetric","record","recordWorkspaceCreated","add","recordWorkspaceMembershipChanged","action","recordWorkspaceInvitationCreated","emailRequested","email_requested","recordWorkspaceInvitationAccepted","outcome"],"mappings":"AAAA,SAAQA,eAAe,QAAO,8BAA8B;AAM5D,MAAMC,QAAQD,gBAAgBE,QAAQ,CAAC;AAEvC,MAAMC,wBAAwBF,MAAMG,aAAa,CAAwB,sBAAsB;IAC7FC,aAAa;AACf;AAEA,MAAMC,yBAAyBL,MAAMG,aAAa,CAE/C,iCAAiC;IAClCC,aAAa;AACf;AAEA,MAAME,yBAAyBN,MAAMG,aAAa,CAE/C,iCAAiC;IAClCC,aAAa;AACf;AAEA,MAAMG,0BAA0BP,MAAMG,aAAa,CAEhD,kCAAkC;IACnCC,aAAa;AACf;AAEA,SAASI,aAAaC,MAAkB;IACtC,IAAI;QACFA;IACF,EAAE,OAAM;IACN,+CAA+C;IACjD;AACF;AAEA,OAAO,SAASC;IACdF,aAAa,IAAMN,sBAAsBS,GAAG,CAAC;AAC/C;AAEA,OAAO,SAASC,iCAAiCC,MAAwC;IACvFL,aAAa,IAAMH,uBAAuBM,GAAG,CAAC,GAAG;YAACE;QAAM;AAC1D;AAEA,OAAO,SAASC,iCACdC,cAAkD;IAElDP,aAAa,IAAMF,uBAAuBK,GAAG,CAAC,GAAG;YAACK,iBAAiBD;QAAc;AACnF;AAEA,OAAO,SAASE,kCACdC,OAA0C;IAE1CV,aAAa,IAAMD,wBAAwBI,GAAG,CAAC,GAAG;YAACO;QAAO;AAC5D"}
1
+ {"version":3,"sources":["../../src/metrics/instance.ts"],"sourcesContent":["import {instanceMetrics} from '@shipfox/node-opentelemetry';\n\nexport type WorkspacesInvitationEmailRequested = 'requested' | 'skipped';\nexport type WorkspacesMembershipChangeAction = 'added' | 'removed';\nexport type WorkspacesInvitationAcceptOutcome = 'added' | 'already_member';\nexport type WorkspacesRateLimitOutcome = 'allowed' | 'blocked' | 'unavailable';\n\nconst meter = instanceMetrics.getMeter('workspaces');\n\nconst workspaceCreatedCount = meter.createCounter<Record<string, never>>('workspaces_created', {\n description: 'Workspaces created',\n});\n\nconst membershipChangedCount = meter.createCounter<{\n action: WorkspacesMembershipChangeAction;\n}>('workspaces_membership_changed', {\n description: 'Workspace membership changes by action',\n});\n\nconst invitationCreatedCount = meter.createCounter<{\n email_requested: WorkspacesInvitationEmailRequested;\n}>('workspaces_invitation_created', {\n description: 'Workspace invitations created by whether an invitation email was requested',\n});\n\nconst invitationAcceptedCount = meter.createCounter<{\n outcome: WorkspacesInvitationAcceptOutcome;\n}>('workspaces_invitation_accepted', {\n description: 'Workspace invitations accepted by membership outcome',\n});\n\nconst workspaceRateLimitCheckCount = meter.createCounter<{\n action: 'slug-availability';\n outcome: WorkspacesRateLimitOutcome;\n}>('workspaces_rate_limit_checks', {\n description: 'Workspace rate-limit checks by action and outcome',\n});\n\nconst workspaceRateLimitPruneFailureCount = meter.createCounter(\n 'workspaces_rate_limit_prune_failures',\n {\n description: 'Workspace rate-limit prune failures',\n },\n);\n\nfunction recordMetric(record: () => void): void {\n try {\n record();\n } catch {\n // Metrics must not affect workspace mutations.\n }\n}\n\nexport function recordWorkspaceCreated(): void {\n recordMetric(() => workspaceCreatedCount.add(1));\n}\n\nexport function recordWorkspaceMembershipChanged(action: WorkspacesMembershipChangeAction): void {\n recordMetric(() => membershipChangedCount.add(1, {action}));\n}\n\nexport function recordWorkspaceInvitationCreated(\n emailRequested: WorkspacesInvitationEmailRequested,\n): void {\n recordMetric(() => invitationCreatedCount.add(1, {email_requested: emailRequested}));\n}\n\nexport function recordWorkspaceInvitationAccepted(\n outcome: WorkspacesInvitationAcceptOutcome,\n): void {\n recordMetric(() => invitationAcceptedCount.add(1, {outcome}));\n}\n\nexport function recordWorkspaceRateLimitCheck(params: {\n action: 'slug-availability';\n outcome: WorkspacesRateLimitOutcome;\n}): void {\n recordMetric(() => workspaceRateLimitCheckCount.add(1, params));\n}\n\nexport function recordWorkspaceRateLimitPruneFailure(): void {\n recordMetric(() => workspaceRateLimitPruneFailureCount.add(1));\n}\n"],"names":["instanceMetrics","meter","getMeter","workspaceCreatedCount","createCounter","description","membershipChangedCount","invitationCreatedCount","invitationAcceptedCount","workspaceRateLimitCheckCount","workspaceRateLimitPruneFailureCount","recordMetric","record","recordWorkspaceCreated","add","recordWorkspaceMembershipChanged","action","recordWorkspaceInvitationCreated","emailRequested","email_requested","recordWorkspaceInvitationAccepted","outcome","recordWorkspaceRateLimitCheck","params","recordWorkspaceRateLimitPruneFailure"],"mappings":"AAAA,SAAQA,eAAe,QAAO,8BAA8B;AAO5D,MAAMC,QAAQD,gBAAgBE,QAAQ,CAAC;AAEvC,MAAMC,wBAAwBF,MAAMG,aAAa,CAAwB,sBAAsB;IAC7FC,aAAa;AACf;AAEA,MAAMC,yBAAyBL,MAAMG,aAAa,CAE/C,iCAAiC;IAClCC,aAAa;AACf;AAEA,MAAME,yBAAyBN,MAAMG,aAAa,CAE/C,iCAAiC;IAClCC,aAAa;AACf;AAEA,MAAMG,0BAA0BP,MAAMG,aAAa,CAEhD,kCAAkC;IACnCC,aAAa;AACf;AAEA,MAAMI,+BAA+BR,MAAMG,aAAa,CAGrD,gCAAgC;IACjCC,aAAa;AACf;AAEA,MAAMK,sCAAsCT,MAAMG,aAAa,CAC7D,wCACA;IACEC,aAAa;AACf;AAGF,SAASM,aAAaC,MAAkB;IACtC,IAAI;QACFA;IACF,EAAE,OAAM;IACN,+CAA+C;IACjD;AACF;AAEA,OAAO,SAASC;IACdF,aAAa,IAAMR,sBAAsBW,GAAG,CAAC;AAC/C;AAEA,OAAO,SAASC,iCAAiCC,MAAwC;IACvFL,aAAa,IAAML,uBAAuBQ,GAAG,CAAC,GAAG;YAACE;QAAM;AAC1D;AAEA,OAAO,SAASC,iCACdC,cAAkD;IAElDP,aAAa,IAAMJ,uBAAuBO,GAAG,CAAC,GAAG;YAACK,iBAAiBD;QAAc;AACnF;AAEA,OAAO,SAASE,kCACdC,OAA0C;IAE1CV,aAAa,IAAMH,wBAAwBM,GAAG,CAAC,GAAG;YAACO;QAAO;AAC5D;AAEA,OAAO,SAASC,8BAA8BC,MAG7C;IACCZ,aAAa,IAAMF,6BAA6BK,GAAG,CAAC,GAAGS;AACzD;AAEA,OAAO,SAASC;IACdb,aAAa,IAAMD,oCAAoCI,GAAG,CAAC;AAC7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/membership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAE,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AACnG,OAAO,KAAK,EAAC,kBAAkB,EAAE,uBAAuB,EAAC,MAAM,oBAAoB,CAAC;AAEpF,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,uBAAuB,GAClC,0BAA0B,CAU5B;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,GAAG,qBAAqB,CAU7F"}
1
+ {"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/membership.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAE,0BAA0B,EAAC,MAAM,6BAA6B,CAAC;AACnG,OAAO,KAAK,EAAC,kBAAkB,EAAE,uBAAuB,EAAC,MAAM,oBAAoB,CAAC;AAEpF,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,uBAAuB,GAClC,0BAA0B,CAW5B;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,GAAG,qBAAqB,CAU7F"}
@@ -4,6 +4,7 @@ export function toMembershipWithWorkspaceDto(membership) {
4
4
  user_id: membership.userId,
5
5
  workspace_id: membership.workspaceId,
6
6
  workspace_name: membership.workspaceName,
7
+ workspace_slug: membership.workspaceSlug,
7
8
  workspace_status: membership.workspaceStatus,
8
9
  created_at: membership.createdAt.toISOString(),
9
10
  updated_at: membership.updatedAt.toISOString()
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/membership.ts"],"sourcesContent":["import type {MembershipWithUserDto, MembershipWithWorkspaceDto} from '@shipfox/api-workspaces-dto';\nimport type {MembershipWithUser, MembershipWithWorkspace} from '#db/memberships.js';\n\nexport function toMembershipWithWorkspaceDto(\n membership: MembershipWithWorkspace,\n): MembershipWithWorkspaceDto {\n return {\n id: membership.id,\n user_id: membership.userId,\n workspace_id: membership.workspaceId,\n workspace_name: membership.workspaceName,\n workspace_status: membership.workspaceStatus,\n created_at: membership.createdAt.toISOString(),\n updated_at: membership.updatedAt.toISOString(),\n };\n}\n\nexport function toMembershipWithUserDto(membership: MembershipWithUser): MembershipWithUserDto {\n return {\n id: membership.id,\n user_id: membership.userId,\n workspace_id: membership.workspaceId,\n user_email: membership.userEmail,\n user_name: membership.userName,\n created_at: membership.createdAt.toISOString(),\n updated_at: membership.updatedAt.toISOString(),\n };\n}\n"],"names":["toMembershipWithWorkspaceDto","membership","id","user_id","userId","workspace_id","workspaceId","workspace_name","workspaceName","workspace_status","workspaceStatus","created_at","createdAt","toISOString","updated_at","updatedAt","toMembershipWithUserDto","user_email","userEmail","user_name","userName"],"mappings":"AAGA,OAAO,SAASA,6BACdC,UAAmC;IAEnC,OAAO;QACLC,IAAID,WAAWC,EAAE;QACjBC,SAASF,WAAWG,MAAM;QAC1BC,cAAcJ,WAAWK,WAAW;QACpCC,gBAAgBN,WAAWO,aAAa;QACxCC,kBAAkBR,WAAWS,eAAe;QAC5CC,YAAYV,WAAWW,SAAS,CAACC,WAAW;QAC5CC,YAAYb,WAAWc,SAAS,CAACF,WAAW;IAC9C;AACF;AAEA,OAAO,SAASG,wBAAwBf,UAA8B;IACpE,OAAO;QACLC,IAAID,WAAWC,EAAE;QACjBC,SAASF,WAAWG,MAAM;QAC1BC,cAAcJ,WAAWK,WAAW;QACpCW,YAAYhB,WAAWiB,SAAS;QAChCC,WAAWlB,WAAWmB,QAAQ;QAC9BT,YAAYV,WAAWW,SAAS,CAACC,WAAW;QAC5CC,YAAYb,WAAWc,SAAS,CAACF,WAAW;IAC9C;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/membership.ts"],"sourcesContent":["import type {MembershipWithUserDto, MembershipWithWorkspaceDto} from '@shipfox/api-workspaces-dto';\nimport type {MembershipWithUser, MembershipWithWorkspace} from '#db/memberships.js';\n\nexport function toMembershipWithWorkspaceDto(\n membership: MembershipWithWorkspace,\n): MembershipWithWorkspaceDto {\n return {\n id: membership.id,\n user_id: membership.userId,\n workspace_id: membership.workspaceId,\n workspace_name: membership.workspaceName,\n workspace_slug: membership.workspaceSlug,\n workspace_status: membership.workspaceStatus,\n created_at: membership.createdAt.toISOString(),\n updated_at: membership.updatedAt.toISOString(),\n };\n}\n\nexport function toMembershipWithUserDto(membership: MembershipWithUser): MembershipWithUserDto {\n return {\n id: membership.id,\n user_id: membership.userId,\n workspace_id: membership.workspaceId,\n user_email: membership.userEmail,\n user_name: membership.userName,\n created_at: membership.createdAt.toISOString(),\n updated_at: membership.updatedAt.toISOString(),\n };\n}\n"],"names":["toMembershipWithWorkspaceDto","membership","id","user_id","userId","workspace_id","workspaceId","workspace_name","workspaceName","workspace_slug","workspaceSlug","workspace_status","workspaceStatus","created_at","createdAt","toISOString","updated_at","updatedAt","toMembershipWithUserDto","user_email","userEmail","user_name","userName"],"mappings":"AAGA,OAAO,SAASA,6BACdC,UAAmC;IAEnC,OAAO;QACLC,IAAID,WAAWC,EAAE;QACjBC,SAASF,WAAWG,MAAM;QAC1BC,cAAcJ,WAAWK,WAAW;QACpCC,gBAAgBN,WAAWO,aAAa;QACxCC,gBAAgBR,WAAWS,aAAa;QACxCC,kBAAkBV,WAAWW,eAAe;QAC5CC,YAAYZ,WAAWa,SAAS,CAACC,WAAW;QAC5CC,YAAYf,WAAWgB,SAAS,CAACF,WAAW;IAC9C;AACF;AAEA,OAAO,SAASG,wBAAwBjB,UAA8B;IACpE,OAAO;QACLC,IAAID,WAAWC,EAAE;QACjBC,SAASF,WAAWG,MAAM;QAC1BC,cAAcJ,WAAWK,WAAW;QACpCa,YAAYlB,WAAWmB,SAAS;QAChCC,WAAWpB,WAAWqB,QAAQ;QAC9BT,YAAYZ,WAAWa,SAAS,CAACC,WAAW;QAC5CC,YAAYf,WAAWgB,SAAS,CAACF,WAAW;IAC9C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAE3D,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,CASjE"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAE3D,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,CAUjE"}
@@ -2,6 +2,7 @@ export function toWorkspaceDto(workspace) {
2
2
  return {
3
3
  id: workspace.id,
4
4
  name: workspace.name,
5
+ slug: workspace.slug,
5
6
  status: workspace.status,
6
7
  settings: workspace.settings,
7
8
  created_at: workspace.createdAt.toISOString(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/workspace.ts"],"sourcesContent":["import type {WorkspaceDto} from '@shipfox/api-workspaces-dto';\nimport type {Workspace} from '#core/entities/workspace.js';\n\nexport function toWorkspaceDto(workspace: Workspace): WorkspaceDto {\n return {\n id: workspace.id,\n name: workspace.name,\n status: workspace.status,\n settings: workspace.settings,\n created_at: workspace.createdAt.toISOString(),\n updated_at: workspace.updatedAt.toISOString(),\n };\n}\n"],"names":["toWorkspaceDto","workspace","id","name","status","settings","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAGA,OAAO,SAASA,eAAeC,SAAoB;IACjD,OAAO;QACLC,IAAID,UAAUC,EAAE;QAChBC,MAAMF,UAAUE,IAAI;QACpBC,QAAQH,UAAUG,MAAM;QACxBC,UAAUJ,UAAUI,QAAQ;QAC5BC,YAAYL,UAAUM,SAAS,CAACC,WAAW;QAC3CC,YAAYR,UAAUS,SAAS,CAACF,WAAW;IAC7C;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/workspace.ts"],"sourcesContent":["import type {WorkspaceDto} from '@shipfox/api-workspaces-dto';\nimport type {Workspace} from '#core/entities/workspace.js';\n\nexport function toWorkspaceDto(workspace: Workspace): WorkspaceDto {\n return {\n id: workspace.id,\n name: workspace.name,\n slug: workspace.slug,\n status: workspace.status,\n settings: workspace.settings,\n created_at: workspace.createdAt.toISOString(),\n updated_at: workspace.updatedAt.toISOString(),\n };\n}\n"],"names":["toWorkspaceDto","workspace","id","name","slug","status","settings","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAGA,OAAO,SAASA,eAAeC,SAAoB;IACjD,OAAO;QACLC,IAAID,UAAUC,EAAE;QAChBC,MAAMF,UAAUE,IAAI;QACpBC,MAAMH,UAAUG,IAAI;QACpBC,QAAQJ,UAAUI,MAAM;QACxBC,UAAUL,UAAUK,QAAQ;QAC5BC,YAAYN,UAAUO,SAAS,CAACC,WAAW;QAC3CC,YAAYT,UAAUU,SAAS,CAACF,WAAW;IAC7C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../../../src/presentation/e2eRoutes/create-workspace.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,uBAAuB,iDAqBlC,CAAC"}
1
+ {"version":3,"file":"create-workspace.d.ts","sourceRoot":"","sources":["../../../src/presentation/e2eRoutes/create-workspace.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,uBAAuB,iDA4BlC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { e2eCreateWorkspaceBodySchema, e2eCreateWorkspaceResponseSchema } from '@shipfox/api-workspaces-dto';
2
- import { defineRoute } from '@shipfox/node-fastify';
2
+ import { ClientError, defineRoute } from '@shipfox/node-fastify';
3
+ import { WorkspaceSlugConflictError } from '#core/errors.js';
3
4
  import { createWorkspaceForUser } from '#core/index.js';
4
5
  import { toWorkspaceDto } from '#presentation/dto/index.js';
5
6
  export const createE2eWorkspaceRoute = defineRoute({
@@ -12,9 +13,18 @@ export const createE2eWorkspaceRoute = defineRoute({
12
13
  201: e2eCreateWorkspaceResponseSchema
13
14
  }
14
15
  },
16
+ errorHandler: (error)=>{
17
+ if (error instanceof WorkspaceSlugConflictError) {
18
+ throw new ClientError('Workspace slug is already taken', 'slug-conflict', {
19
+ status: 409
20
+ });
21
+ }
22
+ throw error;
23
+ },
15
24
  handler: async (request, reply)=>{
16
25
  const workspace = await createWorkspaceForUser({
17
26
  name: request.body.name,
27
+ slug: request.body.slug ?? `e2e-workspace-${crypto.randomUUID().slice(0, 8)}`,
18
28
  userId: request.body.user_id,
19
29
  userEmail: request.body.user_email,
20
30
  userName: request.body.user_name
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/e2eRoutes/create-workspace.ts"],"sourcesContent":["import {\n e2eCreateWorkspaceBodySchema,\n e2eCreateWorkspaceResponseSchema,\n} from '@shipfox/api-workspaces-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {createWorkspaceForUser} from '#core/index.js';\nimport {toWorkspaceDto} from '#presentation/dto/index.js';\n\nexport const createE2eWorkspaceRoute = defineRoute({\n method: 'POST',\n path: '/',\n description: 'Create a workspace for an existing user for E2E tests.',\n schema: {\n body: e2eCreateWorkspaceBodySchema,\n response: {\n 201: e2eCreateWorkspaceResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const workspace = await createWorkspaceForUser({\n name: request.body.name,\n userId: request.body.user_id,\n userEmail: request.body.user_email,\n userName: request.body.user_name,\n });\n\n reply.code(201);\n return toWorkspaceDto(workspace);\n },\n});\n"],"names":["e2eCreateWorkspaceBodySchema","e2eCreateWorkspaceResponseSchema","defineRoute","createWorkspaceForUser","toWorkspaceDto","createE2eWorkspaceRoute","method","path","description","schema","body","response","handler","request","reply","workspace","name","userId","user_id","userEmail","user_email","userName","user_name","code"],"mappings":"AAAA,SACEA,4BAA4B,EAC5BC,gCAAgC,QAC3B,8BAA8B;AACrC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,cAAc,QAAO,6BAA6B;AAE1D,OAAO,MAAMC,0BAA0BH,YAAY;IACjDI,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMV;QACNW,UAAU;YACR,KAAKV;QACP;IACF;IACAW,SAAS,OAAOC,SAASC;QACvB,MAAMC,YAAY,MAAMZ,uBAAuB;YAC7Ca,MAAMH,QAAQH,IAAI,CAACM,IAAI;YACvBC,QAAQJ,QAAQH,IAAI,CAACQ,OAAO;YAC5BC,WAAWN,QAAQH,IAAI,CAACU,UAAU;YAClCC,UAAUR,QAAQH,IAAI,CAACY,SAAS;QAClC;QAEAR,MAAMS,IAAI,CAAC;QACX,OAAOnB,eAAeW;IACxB;AACF,GAAG"}
1
+ {"version":3,"sources":["../../../src/presentation/e2eRoutes/create-workspace.ts"],"sourcesContent":["import {\n e2eCreateWorkspaceBodySchema,\n e2eCreateWorkspaceResponseSchema,\n} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {WorkspaceSlugConflictError} from '#core/errors.js';\nimport {createWorkspaceForUser} from '#core/index.js';\nimport {toWorkspaceDto} from '#presentation/dto/index.js';\n\nexport const createE2eWorkspaceRoute = defineRoute({\n method: 'POST',\n path: '/',\n description: 'Create a workspace for an existing user for E2E tests.',\n schema: {\n body: e2eCreateWorkspaceBodySchema,\n response: {\n 201: e2eCreateWorkspaceResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceSlugConflictError) {\n throw new ClientError('Workspace slug is already taken', 'slug-conflict', {status: 409});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const workspace = await createWorkspaceForUser({\n name: request.body.name,\n slug: request.body.slug ?? `e2e-workspace-${crypto.randomUUID().slice(0, 8)}`,\n userId: request.body.user_id,\n userEmail: request.body.user_email,\n userName: request.body.user_name,\n });\n\n reply.code(201);\n return toWorkspaceDto(workspace);\n },\n});\n"],"names":["e2eCreateWorkspaceBodySchema","e2eCreateWorkspaceResponseSchema","ClientError","defineRoute","WorkspaceSlugConflictError","createWorkspaceForUser","toWorkspaceDto","createE2eWorkspaceRoute","method","path","description","schema","body","response","errorHandler","error","status","handler","request","reply","workspace","name","slug","crypto","randomUUID","slice","userId","user_id","userEmail","user_email","userName","user_name","code"],"mappings":"AAAA,SACEA,4BAA4B,EAC5BC,gCAAgC,QAC3B,8BAA8B;AACrC,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,0BAA0B,QAAO,kBAAkB;AAC3D,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,cAAc,QAAO,6BAA6B;AAE1D,OAAO,MAAMC,0BAA0BJ,YAAY;IACjDK,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,MAAMZ;QACNa,UAAU;YACR,KAAKZ;QACP;IACF;IACAa,cAAc,CAACC;QACb,IAAIA,iBAAiBX,4BAA4B;YAC/C,MAAM,IAAIF,YAAY,mCAAmC,iBAAiB;gBAACc,QAAQ;YAAG;QACxF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAMC,YAAY,MAAMf,uBAAuB;YAC7CgB,MAAMH,QAAQN,IAAI,CAACS,IAAI;YACvBC,MAAMJ,QAAQN,IAAI,CAACU,IAAI,IAAI,CAAC,cAAc,EAAEC,OAAOC,UAAU,GAAGC,KAAK,CAAC,GAAG,IAAI;YAC7EC,QAAQR,QAAQN,IAAI,CAACe,OAAO;YAC5BC,WAAWV,QAAQN,IAAI,CAACiB,UAAU;YAClCC,UAAUZ,QAAQN,IAAI,CAACmB,SAAS;QAClC;QAEAZ,MAAMa,IAAI,CAAC;QACX,OAAO1B,eAAec;IACxB;AACF,GAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AACjF,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AACpF,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAMtD,eAAO,MAAM,gBAAgB,EAAE,UAAU,EAiBxC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,wBAAwB,CAAC;CACnC,GAAG,UAAU,EAAE,CAEf"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wCAAwC,CAAC;AACjF,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,uCAAuC,CAAC;AACpF,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAWtD,eAAO,MAAM,gBAAgB,EAAE,UAAU,EAmBxC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,wBAAwB,CAAC;CACnC,GAAG,UAAU,EAAE,CAEf"}
@@ -1,7 +1,7 @@
1
1
  import { createAdminWorkspacesRoutes } from './admin-workspaces.js';
2
2
  import { invitationsAcceptGroup, invitationsWorkspaceScopedRoutes } from './invitations/index.js';
3
3
  import { memberRoutes } from './members/index.js';
4
- import { createWorkspaceRoute, listUserWorkspacesRoute } from './workspaces/index.js';
4
+ import { createWorkspaceRoute, listUserWorkspacesRoute, updateWorkspaceRoute, workspaceSlugAvailabilityRoute } from './workspaces/index.js';
5
5
  export const workspacesRoutes = [
6
6
  invitationsAcceptGroup,
7
7
  {
@@ -9,6 +9,8 @@ export const workspacesRoutes = [
9
9
  routes: [
10
10
  listUserWorkspacesRoute,
11
11
  createWorkspaceRoute,
12
+ workspaceSlugAvailabilityRoute,
13
+ updateWorkspaceRoute,
12
14
  {
13
15
  prefix: '/:workspaceId/members',
14
16
  routes: memberRoutes
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createAdminWorkspacesRoutes} from './admin-workspaces.js';\nimport {invitationsAcceptGroup, invitationsWorkspaceScopedRoutes} from './invitations/index.js';\nimport {memberRoutes} from './members/index.js';\nimport {createWorkspaceRoute, listUserWorkspacesRoute} from './workspaces/index.js';\n\nexport const workspacesRoutes: RouteGroup[] = [\n invitationsAcceptGroup,\n {\n prefix: '/workspaces',\n routes: [\n listUserWorkspacesRoute,\n createWorkspaceRoute,\n {\n prefix: '/:workspaceId/members',\n routes: memberRoutes,\n },\n {\n prefix: '/:workspaceId/invitations',\n routes: invitationsWorkspaceScopedRoutes,\n },\n ],\n },\n];\n\nexport function createWorkspacesRoutes(params: {\n auth: AuthInterModuleClient;\n projects: ProjectsModuleClient;\n runners: RunnersInterModuleClient;\n}): RouteGroup[] {\n return [...workspacesRoutes, createAdminWorkspacesRoutes(params)];\n}\n"],"names":["createAdminWorkspacesRoutes","invitationsAcceptGroup","invitationsWorkspaceScopedRoutes","memberRoutes","createWorkspaceRoute","listUserWorkspacesRoute","workspacesRoutes","prefix","routes","createWorkspacesRoutes","params"],"mappings":"AAIA,SAAQA,2BAA2B,QAAO,wBAAwB;AAClE,SAAQC,sBAAsB,EAAEC,gCAAgC,QAAO,yBAAyB;AAChG,SAAQC,YAAY,QAAO,qBAAqB;AAChD,SAAQC,oBAAoB,EAAEC,uBAAuB,QAAO,wBAAwB;AAEpF,OAAO,MAAMC,mBAAiC;IAC5CL;IACA;QACEM,QAAQ;QACRC,QAAQ;YACNH;YACAD;YACA;gBACEG,QAAQ;gBACRC,QAAQL;YACV;YACA;gBACEI,QAAQ;gBACRC,QAAQN;YACV;SACD;IACH;CACD,CAAC;AAEF,OAAO,SAASO,uBAAuBC,MAItC;IACC,OAAO;WAAIJ;QAAkBN,4BAA4BU;KAAQ;AACnE"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';\nimport type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createAdminWorkspacesRoutes} from './admin-workspaces.js';\nimport {invitationsAcceptGroup, invitationsWorkspaceScopedRoutes} from './invitations/index.js';\nimport {memberRoutes} from './members/index.js';\nimport {\n createWorkspaceRoute,\n listUserWorkspacesRoute,\n updateWorkspaceRoute,\n workspaceSlugAvailabilityRoute,\n} from './workspaces/index.js';\n\nexport const workspacesRoutes: RouteGroup[] = [\n invitationsAcceptGroup,\n {\n prefix: '/workspaces',\n routes: [\n listUserWorkspacesRoute,\n createWorkspaceRoute,\n workspaceSlugAvailabilityRoute,\n updateWorkspaceRoute,\n {\n prefix: '/:workspaceId/members',\n routes: memberRoutes,\n },\n {\n prefix: '/:workspaceId/invitations',\n routes: invitationsWorkspaceScopedRoutes,\n },\n ],\n },\n];\n\nexport function createWorkspacesRoutes(params: {\n auth: AuthInterModuleClient;\n projects: ProjectsModuleClient;\n runners: RunnersInterModuleClient;\n}): RouteGroup[] {\n return [...workspacesRoutes, createAdminWorkspacesRoutes(params)];\n}\n"],"names":["createAdminWorkspacesRoutes","invitationsAcceptGroup","invitationsWorkspaceScopedRoutes","memberRoutes","createWorkspaceRoute","listUserWorkspacesRoute","updateWorkspaceRoute","workspaceSlugAvailabilityRoute","workspacesRoutes","prefix","routes","createWorkspacesRoutes","params"],"mappings":"AAIA,SAAQA,2BAA2B,QAAO,wBAAwB;AAClE,SAAQC,sBAAsB,EAAEC,gCAAgC,QAAO,yBAAyB;AAChG,SAAQC,YAAY,QAAO,qBAAqB;AAChD,SACEC,oBAAoB,EACpBC,uBAAuB,EACvBC,oBAAoB,EACpBC,8BAA8B,QACzB,wBAAwB;AAE/B,OAAO,MAAMC,mBAAiC;IAC5CP;IACA;QACEQ,QAAQ;QACRC,QAAQ;YACNL;YACAD;YACAG;YACAD;YACA;gBACEG,QAAQ;gBACRC,QAAQP;YACV;YACA;gBACEM,QAAQ;gBACRC,QAAQR;YACV;SACD;IACH;CACD,CAAC;AAEF,OAAO,SAASS,uBAAuBC,MAItC;IACC,OAAO;WAAIJ;QAAkBR,4BAA4BY;KAAQ;AACnE"}
@@ -0,0 +1,3 @@
1
+ import { type FastifyReply, type FastifyRequest } from '@shipfox/node-fastify';
2
+ export declare function createWorkspaceSlugAvailabilityRateLimitPreHandler(): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
3
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAW1F,wBAAgB,kDAAkD,cACzC,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAgC3E"}
@@ -0,0 +1,42 @@
1
+ import { ClientError } from '@shipfox/node-fastify';
2
+ import { enforceRateLimit as enforceSharedRateLimit } from '@shipfox/node-rate-limit';
3
+ import { checkWorkspacesRateLimit, WORKSPACE_SLUG_AVAILABILITY_RATE_LIMIT } from '#core/rate-limit.js';
4
+ function routeName(request) {
5
+ return request.routeOptions.url ?? request.url.split('?')[0] ?? 'unknown';
6
+ }
7
+ export function createWorkspaceSlugAvailabilityRateLimitPreHandler() {
8
+ return async (request, reply)=>{
9
+ await enforceSharedRateLimit({
10
+ request,
11
+ reply,
12
+ check: ()=>checkWorkspacesRateLimit({
13
+ action: 'slug-availability',
14
+ scope: 'ip',
15
+ identifier: request.ip,
16
+ ...WORKSPACE_SLUG_AVAILABILITY_RATE_LIMIT
17
+ }),
18
+ route: routeName(request),
19
+ unavailableCode: 'workspace-rate-limit-unavailable',
20
+ unavailableMessage: 'Workspace rate limiter unavailable',
21
+ setRetryAfter: (reply, retryAfterSeconds)=>{
22
+ reply.header('Retry-After', String(retryAfterSeconds));
23
+ },
24
+ logWarn: (request, context)=>{
25
+ request.log.warn(context, 'Workspace rate limit blocked request');
26
+ },
27
+ logError: (request, context)=>{
28
+ request.log.error(context, 'Workspace rate limiter unavailable');
29
+ },
30
+ createClientError: (presentation, cause)=>new ClientError(presentation.message, presentation.code, {
31
+ status: presentation.status,
32
+ ...presentation.details ? {
33
+ details: presentation.details
34
+ } : {},
35
+ data: presentation.data,
36
+ cause
37
+ })
38
+ });
39
+ };
40
+ }
41
+
42
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/routes/rate-limit.ts"],"sourcesContent":["import {ClientError, type FastifyReply, type FastifyRequest} from '@shipfox/node-fastify';\nimport {enforceRateLimit as enforceSharedRateLimit} from '@shipfox/node-rate-limit';\nimport {\n checkWorkspacesRateLimit,\n WORKSPACE_SLUG_AVAILABILITY_RATE_LIMIT,\n} from '#core/rate-limit.js';\n\nfunction routeName(request: FastifyRequest): string {\n return request.routeOptions.url ?? request.url.split('?')[0] ?? 'unknown';\n}\n\nexport function createWorkspaceSlugAvailabilityRateLimitPreHandler() {\n return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {\n await enforceSharedRateLimit({\n request,\n reply,\n check: () =>\n checkWorkspacesRateLimit({\n action: 'slug-availability',\n scope: 'ip',\n identifier: request.ip,\n ...WORKSPACE_SLUG_AVAILABILITY_RATE_LIMIT,\n }),\n route: routeName(request),\n unavailableCode: 'workspace-rate-limit-unavailable',\n unavailableMessage: 'Workspace rate limiter unavailable',\n setRetryAfter: (reply, retryAfterSeconds) => {\n reply.header('Retry-After', String(retryAfterSeconds));\n },\n logWarn: (request, context) => {\n request.log.warn(context, 'Workspace rate limit blocked request');\n },\n logError: (request, context) => {\n request.log.error(context, 'Workspace rate limiter unavailable');\n },\n createClientError: (presentation, cause) =>\n new ClientError(presentation.message, presentation.code, {\n status: presentation.status,\n ...(presentation.details ? {details: presentation.details} : {}),\n data: presentation.data,\n cause,\n }),\n });\n };\n}\n"],"names":["ClientError","enforceRateLimit","enforceSharedRateLimit","checkWorkspacesRateLimit","WORKSPACE_SLUG_AVAILABILITY_RATE_LIMIT","routeName","request","routeOptions","url","split","createWorkspaceSlugAvailabilityRateLimitPreHandler","reply","check","action","scope","identifier","ip","route","unavailableCode","unavailableMessage","setRetryAfter","retryAfterSeconds","header","String","logWarn","context","log","warn","logError","error","createClientError","presentation","cause","message","code","status","details","data"],"mappings":"AAAA,SAAQA,WAAW,QAA+C,wBAAwB;AAC1F,SAAQC,oBAAoBC,sBAAsB,QAAO,2BAA2B;AACpF,SACEC,wBAAwB,EACxBC,sCAAsC,QACjC,sBAAsB;AAE7B,SAASC,UAAUC,OAAuB;IACxC,OAAOA,QAAQC,YAAY,CAACC,GAAG,IAAIF,QAAQE,GAAG,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI;AAClE;AAEA,OAAO,SAASC;IACd,OAAO,OAAOJ,SAAyBK;QACrC,MAAMT,uBAAuB;YAC3BI;YACAK;YACAC,OAAO,IACLT,yBAAyB;oBACvBU,QAAQ;oBACRC,OAAO;oBACPC,YAAYT,QAAQU,EAAE;oBACtB,GAAGZ,sCAAsC;gBAC3C;YACFa,OAAOZ,UAAUC;YACjBY,iBAAiB;YACjBC,oBAAoB;YACpBC,eAAe,CAACT,OAAOU;gBACrBV,MAAMW,MAAM,CAAC,eAAeC,OAAOF;YACrC;YACAG,SAAS,CAAClB,SAASmB;gBACjBnB,QAAQoB,GAAG,CAACC,IAAI,CAACF,SAAS;YAC5B;YACAG,UAAU,CAACtB,SAASmB;gBAClBnB,QAAQoB,GAAG,CAACG,KAAK,CAACJ,SAAS;YAC7B;YACAK,mBAAmB,CAACC,cAAcC,QAChC,IAAIhC,YAAY+B,aAAaE,OAAO,EAAEF,aAAaG,IAAI,EAAE;oBACvDC,QAAQJ,aAAaI,MAAM;oBAC3B,GAAIJ,aAAaK,OAAO,GAAG;wBAACA,SAASL,aAAaK,OAAO;oBAAA,IAAI,CAAC,CAAC;oBAC/DC,MAAMN,aAAaM,IAAI;oBACvBL;gBACF;QACJ;IACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/create.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,iDA6B/B,CAAC"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/create.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,oBAAoB,iDAoC/B,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
2
2
  import { createWorkspaceBodySchema, workspaceResponseSchema } from '@shipfox/api-workspaces-dto';
3
3
  import { ClientError, defineRoute } from '@shipfox/node-fastify';
4
+ import { WorkspaceSlugConflictError } from '#core/errors.js';
4
5
  import { createWorkspaceForUser } from '#core/index.js';
5
6
  import { toWorkspaceDto } from '#presentation/dto/index.js';
6
7
  export const createWorkspaceRoute = defineRoute({
@@ -14,6 +15,14 @@ export const createWorkspaceRoute = defineRoute({
14
15
  201: workspaceResponseSchema
15
16
  }
16
17
  },
18
+ errorHandler: (error)=>{
19
+ if (error instanceof WorkspaceSlugConflictError) {
20
+ throw new ClientError('Workspace slug is already taken', 'slug-conflict', {
21
+ status: 409
22
+ });
23
+ }
24
+ throw error;
25
+ },
17
26
  handler: async (request, reply)=>{
18
27
  const client = getUserContext(request);
19
28
  if (!client) {
@@ -21,9 +30,10 @@ export const createWorkspaceRoute = defineRoute({
21
30
  status: 401
22
31
  });
23
32
  }
24
- const { name } = request.body;
33
+ const { name, slug } = request.body;
25
34
  const workspace = await createWorkspaceForUser({
26
35
  name,
36
+ slug,
27
37
  userId: client.userId,
28
38
  userEmail: client.email,
29
39
  userName: client.name
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/presentation/routes/workspaces/create.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {createWorkspaceBodySchema, workspaceResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {createWorkspaceForUser} from '#core/index.js';\nimport {toWorkspaceDto} from '#presentation/dto/index.js';\n\nexport const createWorkspaceRoute = defineRoute({\n method: 'POST',\n path: '/',\n description: 'Create a workspace for the signed-in user.',\n auth: AUTH_USER,\n schema: {\n body: createWorkspaceBodySchema,\n response: {\n 201: workspaceResponseSchema,\n },\n },\n handler: async (request, reply) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {name} = request.body;\n\n const workspace = await createWorkspaceForUser({\n name,\n userId: client.userId,\n userEmail: client.email,\n userName: client.name,\n });\n\n reply.code(201);\n return toWorkspaceDto(workspace);\n },\n});\n"],"names":["AUTH_USER","getUserContext","createWorkspaceBodySchema","workspaceResponseSchema","ClientError","defineRoute","createWorkspaceForUser","toWorkspaceDto","createWorkspaceRoute","method","path","description","auth","schema","body","response","handler","request","reply","client","status","name","workspace","userId","userEmail","email","userName","code"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,yBAAyB,EAAEC,uBAAuB,QAAO,8BAA8B;AAC/F,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,cAAc,QAAO,6BAA6B;AAE1D,OAAO,MAAMC,uBAAuBH,YAAY;IAC9CI,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMZ;IACNa,QAAQ;QACNC,MAAMZ;QACNa,UAAU;YACR,KAAKZ;QACP;IACF;IACAa,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAASlB,eAAegB;QAC9B,IAAI,CAACE,QAAQ;YACX,MAAM,IAAIf,YAAY,2BAA2B,gBAAgB;gBAACgB,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACC,IAAI,EAAC,GAAGJ,QAAQH,IAAI;QAE3B,MAAMQ,YAAY,MAAMhB,uBAAuB;YAC7Ce;YACAE,QAAQJ,OAAOI,MAAM;YACrBC,WAAWL,OAAOM,KAAK;YACvBC,UAAUP,OAAOE,IAAI;QACvB;QAEAH,MAAMS,IAAI,CAAC;QACX,OAAOpB,eAAee;IACxB;AACF,GAAG"}
1
+ {"version":3,"sources":["../../../../src/presentation/routes/workspaces/create.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {createWorkspaceBodySchema, workspaceResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {WorkspaceSlugConflictError} from '#core/errors.js';\nimport {createWorkspaceForUser} from '#core/index.js';\nimport {toWorkspaceDto} from '#presentation/dto/index.js';\n\nexport const createWorkspaceRoute = defineRoute({\n method: 'POST',\n path: '/',\n description: 'Create a workspace for the signed-in user.',\n auth: AUTH_USER,\n schema: {\n body: createWorkspaceBodySchema,\n response: {\n 201: workspaceResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceSlugConflictError) {\n throw new ClientError('Workspace slug is already taken', 'slug-conflict', {status: 409});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {name, slug} = request.body;\n\n const workspace = await createWorkspaceForUser({\n name,\n slug,\n userId: client.userId,\n userEmail: client.email,\n userName: client.name,\n });\n\n reply.code(201);\n return toWorkspaceDto(workspace);\n },\n});\n"],"names":["AUTH_USER","getUserContext","createWorkspaceBodySchema","workspaceResponseSchema","ClientError","defineRoute","WorkspaceSlugConflictError","createWorkspaceForUser","toWorkspaceDto","createWorkspaceRoute","method","path","description","auth","schema","body","response","errorHandler","error","status","handler","request","reply","client","name","slug","workspace","userId","userEmail","email","userName","code"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,yBAAyB,EAAEC,uBAAuB,QAAO,8BAA8B;AAC/F,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,0BAA0B,QAAO,kBAAkB;AAC3D,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,cAAc,QAAO,6BAA6B;AAE1D,OAAO,MAAMC,uBAAuBJ,YAAY;IAC9CK,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMb;IACNc,QAAQ;QACNC,MAAMb;QACNc,UAAU;YACR,KAAKb;QACP;IACF;IACAc,cAAc,CAACC;QACb,IAAIA,iBAAiBZ,4BAA4B;YAC/C,MAAM,IAAIF,YAAY,mCAAmC,iBAAiB;gBAACe,QAAQ;YAAG;QACxF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAAStB,eAAeoB;QAC9B,IAAI,CAACE,QAAQ;YACX,MAAM,IAAInB,YAAY,2BAA2B,gBAAgB;gBAACe,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACK,IAAI,EAAEC,IAAI,EAAC,GAAGJ,QAAQN,IAAI;QAEjC,MAAMW,YAAY,MAAMnB,uBAAuB;YAC7CiB;YACAC;YACAE,QAAQJ,OAAOI,MAAM;YACrBC,WAAWL,OAAOM,KAAK;YACvBC,UAAUP,OAAOC,IAAI;QACvB;QAEAF,MAAMS,IAAI,CAAC;QACX,OAAOvB,eAAekB;IACxB;AACF,GAAG"}
@@ -1,3 +1,5 @@
1
1
  export { createWorkspaceRoute } from './create.js';
2
2
  export { listUserWorkspacesRoute } from './list.js';
3
+ export { workspaceSlugAvailabilityRoute } from './slug-availability.js';
4
+ export { updateWorkspaceRoute } from './update.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,aAAa,CAAC;AACjD,OAAO,EAAC,uBAAuB,EAAC,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,aAAa,CAAC;AACjD,OAAO,EAAC,uBAAuB,EAAC,MAAM,WAAW,CAAC;AAClD,OAAO,EAAC,8BAA8B,EAAC,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAC,oBAAoB,EAAC,MAAM,aAAa,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export { createWorkspaceRoute } from './create.js';
2
2
  export { listUserWorkspacesRoute } from './list.js';
3
+ export { workspaceSlugAvailabilityRoute } from './slug-availability.js';
4
+ export { updateWorkspaceRoute } from './update.js';
3
5
 
4
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/presentation/routes/workspaces/index.ts"],"sourcesContent":["export {createWorkspaceRoute} from './create.js';\nexport {listUserWorkspacesRoute} from './list.js';\n"],"names":["createWorkspaceRoute","listUserWorkspacesRoute"],"mappings":"AAAA,SAAQA,oBAAoB,QAAO,cAAc;AACjD,SAAQC,uBAAuB,QAAO,YAAY"}
1
+ {"version":3,"sources":["../../../../src/presentation/routes/workspaces/index.ts"],"sourcesContent":["export {createWorkspaceRoute} from './create.js';\nexport {listUserWorkspacesRoute} from './list.js';\nexport {workspaceSlugAvailabilityRoute} from './slug-availability.js';\nexport {updateWorkspaceRoute} from './update.js';\n"],"names":["createWorkspaceRoute","listUserWorkspacesRoute","workspaceSlugAvailabilityRoute","updateWorkspaceRoute"],"mappings":"AAAA,SAAQA,oBAAoB,QAAO,cAAc;AACjD,SAAQC,uBAAuB,QAAO,YAAY;AAClD,SAAQC,8BAA8B,QAAO,yBAAyB;AACtE,SAAQC,oBAAoB,QAAO,cAAc"}
@@ -0,0 +1,2 @@
1
+ export declare const workspaceSlugAvailabilityRoute: import("@shipfox/node-fastify").RouteDefinition;
2
+ //# sourceMappingURL=slug-availability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-availability.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/slug-availability.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,8BAA8B,iDAoBzC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
2
+ import { workspaceSlugAvailabilityQuerySchema, workspaceSlugAvailabilityResponseSchema } from '@shipfox/api-workspaces-dto';
3
+ import { ClientError, defineRoute } from '@shipfox/node-fastify';
4
+ import { checkWorkspaceSlugAvailability } from '#core/index.js';
5
+ import { createWorkspaceSlugAvailabilityRateLimitPreHandler } from '../rate-limit.js';
6
+ export const workspaceSlugAvailabilityRoute = defineRoute({
7
+ method: 'GET',
8
+ path: '/slug-availability',
9
+ description: 'Check whether a workspace slug is available.',
10
+ auth: AUTH_USER,
11
+ schema: {
12
+ querystring: workspaceSlugAvailabilityQuerySchema,
13
+ response: {
14
+ 200: workspaceSlugAvailabilityResponseSchema
15
+ }
16
+ },
17
+ preHandler: createWorkspaceSlugAvailabilityRateLimitPreHandler(),
18
+ handler: async (request)=>{
19
+ const client = getUserContext(request);
20
+ if (!client) {
21
+ throw new ClientError('Authentication required', 'unauthorized', {
22
+ status: 401
23
+ });
24
+ }
25
+ return {
26
+ available: await checkWorkspaceSlugAvailability(request.query.slug)
27
+ };
28
+ }
29
+ });
30
+
31
+ //# sourceMappingURL=slug-availability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/presentation/routes/workspaces/slug-availability.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {\n workspaceSlugAvailabilityQuerySchema,\n workspaceSlugAvailabilityResponseSchema,\n} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {checkWorkspaceSlugAvailability} from '#core/index.js';\nimport {createWorkspaceSlugAvailabilityRateLimitPreHandler} from '../rate-limit.js';\n\nexport const workspaceSlugAvailabilityRoute = defineRoute({\n method: 'GET',\n path: '/slug-availability',\n description: 'Check whether a workspace slug is available.',\n auth: AUTH_USER,\n schema: {\n querystring: workspaceSlugAvailabilityQuerySchema,\n response: {\n 200: workspaceSlugAvailabilityResponseSchema,\n },\n },\n preHandler: createWorkspaceSlugAvailabilityRateLimitPreHandler(),\n handler: async (request) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n return {available: await checkWorkspaceSlugAvailability(request.query.slug)};\n },\n});\n"],"names":["AUTH_USER","getUserContext","workspaceSlugAvailabilityQuerySchema","workspaceSlugAvailabilityResponseSchema","ClientError","defineRoute","checkWorkspaceSlugAvailability","createWorkspaceSlugAvailabilityRateLimitPreHandler","workspaceSlugAvailabilityRoute","method","path","description","auth","schema","querystring","response","preHandler","handler","request","client","status","available","query","slug"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SACEC,oCAAoC,EACpCC,uCAAuC,QAClC,8BAA8B;AACrC,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,8BAA8B,QAAO,iBAAiB;AAC9D,SAAQC,kDAAkD,QAAO,mBAAmB;AAEpF,OAAO,MAAMC,iCAAiCH,YAAY;IACxDI,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMZ;IACNa,QAAQ;QACNC,aAAaZ;QACba,UAAU;YACR,KAAKZ;QACP;IACF;IACAa,YAAYT;IACZU,SAAS,OAAOC;QACd,MAAMC,SAASlB,eAAeiB;QAC9B,IAAI,CAACC,QAAQ;YACX,MAAM,IAAIf,YAAY,2BAA2B,gBAAgB;gBAACgB,QAAQ;YAAG;QAC/E;QAEA,OAAO;YAACC,WAAW,MAAMf,+BAA+BY,QAAQI,KAAK,CAACC,IAAI;QAAC;IAC7E;AACF,GAAG"}
@@ -0,0 +1,2 @@
1
+ export declare const updateWorkspaceRoute: import("@shipfox/node-fastify").RouteDefinition;
2
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/update.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,oBAAoB,iDAgC/B,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { AUTH_USER, requireWorkspaceAccess } from '@shipfox/api-auth-context';
2
+ import { updateWorkspaceBodySchema, workspaceResponseSchema } from '@shipfox/api-workspaces-dto';
3
+ import { ClientError, defineRoute } from '@shipfox/node-fastify';
4
+ import { z } from 'zod';
5
+ import { WorkspaceNotFoundError, WorkspaceSlugConflictError } from '#core/errors.js';
6
+ import { updateWorkspaceDetails } from '#core/index.js';
7
+ import { toWorkspaceDto } from '#presentation/dto/index.js';
8
+ export const updateWorkspaceRoute = defineRoute({
9
+ method: 'PATCH',
10
+ path: '/:workspaceId',
11
+ description: 'Update workspace details.',
12
+ auth: AUTH_USER,
13
+ schema: {
14
+ params: z.object({
15
+ workspaceId: z.string().uuid()
16
+ }),
17
+ body: updateWorkspaceBodySchema,
18
+ response: {
19
+ 200: workspaceResponseSchema
20
+ }
21
+ },
22
+ errorHandler: (error)=>{
23
+ if (error instanceof WorkspaceNotFoundError) {
24
+ throw new ClientError('Workspace not found', 'not-found', {
25
+ status: 404
26
+ });
27
+ }
28
+ if (error instanceof WorkspaceSlugConflictError) {
29
+ throw new ClientError('Workspace slug is already taken', 'slug-conflict', {
30
+ status: 409
31
+ });
32
+ }
33
+ throw error;
34
+ },
35
+ handler: async (request)=>{
36
+ requireWorkspaceAccess({
37
+ request,
38
+ workspaceId: request.params.workspaceId
39
+ });
40
+ const workspace = await updateWorkspaceDetails({
41
+ workspaceId: request.params.workspaceId,
42
+ name: request.body.name,
43
+ slug: request.body.slug
44
+ });
45
+ return toWorkspaceDto(workspace);
46
+ }
47
+ });
48
+
49
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/presentation/routes/workspaces/update.ts"],"sourcesContent":["import {AUTH_USER, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {updateWorkspaceBodySchema, workspaceResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {WorkspaceNotFoundError, WorkspaceSlugConflictError} from '#core/errors.js';\nimport {updateWorkspaceDetails} from '#core/index.js';\nimport {toWorkspaceDto} from '#presentation/dto/index.js';\n\nexport const updateWorkspaceRoute = defineRoute({\n method: 'PATCH',\n path: '/:workspaceId',\n description: 'Update workspace details.',\n auth: AUTH_USER,\n schema: {\n params: z.object({workspaceId: z.string().uuid()}),\n body: updateWorkspaceBodySchema,\n response: {\n 200: workspaceResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'not-found', {status: 404});\n }\n if (error instanceof WorkspaceSlugConflictError) {\n throw new ClientError('Workspace slug is already taken', 'slug-conflict', {status: 409});\n }\n throw error;\n },\n handler: async (request) => {\n requireWorkspaceAccess({request, workspaceId: request.params.workspaceId});\n\n const workspace = await updateWorkspaceDetails({\n workspaceId: request.params.workspaceId,\n name: request.body.name,\n slug: request.body.slug,\n });\n\n return toWorkspaceDto(workspace);\n },\n});\n"],"names":["AUTH_USER","requireWorkspaceAccess","updateWorkspaceBodySchema","workspaceResponseSchema","ClientError","defineRoute","z","WorkspaceNotFoundError","WorkspaceSlugConflictError","updateWorkspaceDetails","toWorkspaceDto","updateWorkspaceRoute","method","path","description","auth","schema","params","object","workspaceId","string","uuid","body","response","errorHandler","error","status","handler","request","workspace","name","slug"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,sBAAsB,QAAO,4BAA4B;AAC5E,SAAQC,yBAAyB,EAAEC,uBAAuB,QAAO,8BAA8B;AAC/F,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SAAQC,sBAAsB,EAAEC,0BAA0B,QAAO,kBAAkB;AACnF,SAAQC,sBAAsB,QAAO,iBAAiB;AACtD,SAAQC,cAAc,QAAO,6BAA6B;AAE1D,OAAO,MAAMC,uBAAuBN,YAAY;IAC9CO,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMf;IACNgB,QAAQ;QACNC,QAAQX,EAAEY,MAAM,CAAC;YAACC,aAAab,EAAEc,MAAM,GAAGC,IAAI;QAAE;QAChDC,MAAMpB;QACNqB,UAAU;YACR,KAAKpB;QACP;IACF;IACAqB,cAAc,CAACC;QACb,IAAIA,iBAAiBlB,wBAAwB;YAC3C,MAAM,IAAIH,YAAY,uBAAuB,aAAa;gBAACsB,QAAQ;YAAG;QACxE;QACA,IAAID,iBAAiBjB,4BAA4B;YAC/C,MAAM,IAAIJ,YAAY,mCAAmC,iBAAiB;gBAACsB,QAAQ;YAAG;QACxF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC;QACd3B,uBAAuB;YAAC2B;YAAST,aAAaS,QAAQX,MAAM,CAACE,WAAW;QAAA;QAExE,MAAMU,YAAY,MAAMpB,uBAAuB;YAC7CU,aAAaS,QAAQX,MAAM,CAACE,WAAW;YACvCW,MAAMF,QAAQN,IAAI,CAACQ,IAAI;YACvBC,MAAMH,QAAQN,IAAI,CAACS,IAAI;QACzB;QAEA,OAAOrB,eAAemB;IACxB;AACF,GAAG"}