@shipfox/api-projects 9.3.0 → 10.1.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 (47) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +38 -0
  3. package/dist/db/index.d.ts +2 -2
  4. package/dist/db/index.d.ts.map +1 -1
  5. package/dist/db/index.js +1 -1
  6. package/dist/db/index.js.map +1 -1
  7. package/dist/db/projects.d.ts +18 -0
  8. package/dist/db/projects.d.ts.map +1 -1
  9. package/dist/db/projects.js +45 -5
  10. package/dist/db/projects.js.map +1 -1
  11. package/dist/index.d.ts +4 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -3
  14. package/dist/index.js.map +1 -1
  15. package/dist/presentation/dto/index.d.ts +1 -1
  16. package/dist/presentation/dto/index.d.ts.map +1 -1
  17. package/dist/presentation/dto/index.js +1 -1
  18. package/dist/presentation/dto/index.js.map +1 -1
  19. package/dist/presentation/dto/project.d.ts +9 -0
  20. package/dist/presentation/dto/project.d.ts.map +1 -1
  21. package/dist/presentation/dto/project.js +10 -0
  22. package/dist/presentation/dto/project.js.map +1 -1
  23. package/dist/presentation/inter-module.d.ts.map +1 -1
  24. package/dist/presentation/inter-module.js +7 -2
  25. package/dist/presentation/inter-module.js.map +1 -1
  26. package/dist/presentation/routes/admin-projects.d.ts +3 -0
  27. package/dist/presentation/routes/admin-projects.d.ts.map +1 -0
  28. package/dist/presentation/routes/admin-projects.js +61 -0
  29. package/dist/presentation/routes/admin-projects.js.map +1 -0
  30. package/dist/presentation/routes/index.d.ts +2 -1
  31. package/dist/presentation/routes/index.d.ts.map +1 -1
  32. package/dist/presentation/routes/index.js +8 -1
  33. package/dist/presentation/routes/index.js.map +1 -1
  34. package/dist/tsconfig.test.tsbuildinfo +1 -1
  35. package/package.json +6 -5
  36. package/src/db/index.ts +5 -0
  37. package/src/db/projects.test.ts +9 -1
  38. package/src/db/projects.ts +73 -6
  39. package/src/index.ts +8 -2
  40. package/src/presentation/dto/index.ts +1 -1
  41. package/src/presentation/dto/project.ts +12 -0
  42. package/src/presentation/inter-module.ts +4 -1
  43. package/src/presentation/routes/admin-projects.ts +66 -0
  44. package/src/presentation/routes/index.ts +10 -1
  45. package/src/presentation/routes/projects.test.ts +173 -1
  46. package/src/presentation/subscribers/on-source-commit-pushed.test.ts +5 -1
  47. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,6 +1,6 @@
1
1
  $ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
2
- Successfully compiled: 36 files with swc (314.05ms)
3
- 2026-07-26T20:59:45.919Z [INFO] asset workflow-bundle-601f98c1048b285735c6.js 3.17 MiB [emitted] [immutable] (name: main)
2
+ Successfully compiled: 37 files with swc (396.89ms)
3
+ 2026-07-28T09:17:50.507Z [INFO] asset workflow-bundle-601f98c1048b285735c6.js 3.17 MiB [emitted] [immutable] (name: main)
4
4
  orphan modules 33.5 KiB [orphan] 21 modules
5
5
  runtime modules 1.13 KiB 5 modules
6
6
  modules by path ../../../node_modules/.pnpm/ 970 KiB 196 modules
@@ -12,6 +12,6 @@ optional modules 30 bytes [optional]
12
12
  __temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
13
13
  __temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
14
14
  ../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
15
- webpack 5.107.2 compiled successfully in 6598 ms
16
- 2026-07-26T20:59:45.940Z [INFO] Workflow bundle created { size: '3.17MB' }
15
+ webpack 5.107.2 compiled successfully in 6246 ms
16
+ 2026-07-28T09:17:50.533Z [INFO] Workflow bundle created { size: '3.17MB' }
17
17
  /home/runner/work/shipfox/shipfox/libs/api/projects/dist/temporal/workflows/index.js: 3319553 bytes
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @shipfox/api-projects
2
2
 
3
+ ## 10.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 88ae689: Add a bounded, administrator-authorized project summary route with redacted fields and cursor pagination.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [88ae689]
12
+ - Updated dependencies [fb34b6a]
13
+ - @shipfox/api-projects-dto@10.1.0
14
+ - @shipfox/api-auth-dto@10.1.0
15
+ - @shipfox/api-auth-context@10.1.0
16
+
17
+ ## 10.0.0
18
+
19
+ ### Minor Changes
20
+
21
+ - e9280fc: Add an observer-authorized administrator workspace lookup with bounded safe summaries,
22
+ best-effort job counts, and a neutral unavailable-workspace member experience for
23
+ suspended or deleted workspaces.
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [74f9e31]
28
+ - Updated dependencies [e9280fc]
29
+ - @shipfox/node-fastify@0.4.0
30
+ - @shipfox/api-projects-dto@10.0.0
31
+ - @shipfox/api-auth-context@10.0.0
32
+ - @shipfox/node-module@1.0.4
33
+ - @shipfox/api-integration-core-dto@9.0.2
34
+ - @shipfox/inter-module@0.2.2
35
+ - @shipfox/node-drizzle@0.3.4
36
+ - @shipfox/node-opentelemetry@0.6.3
37
+ - @shipfox/node-outbox@0.2.6
38
+ - @shipfox/node-postgres@0.4.4
39
+ - @shipfox/node-temporal@0.4.4
40
+
3
41
  ## 9.3.0
4
42
 
5
43
  ### Patch Changes
@@ -1,7 +1,7 @@
1
1
  export { closeDb, db, schema } from './db.js';
2
2
  export { pruneIntegrationEventDedup, recordIntegrationEventForProject, } from './integration-event-dedup.js';
3
- export type { CreateProjectParams, GetProjectBySourceParams, ListProjectsParams, ListProjectsResult, } from './projects.js';
4
- export { createProject, getProjectById, getProjectBySource, getProjectCount, listProjects, requireProjectForWorkspace, } from './projects.js';
3
+ export type { AdminProjectSummary, CreateProjectParams, GetProjectBySourceParams, ListAdminProjectsParams, ListAdminProjectsResult, ListProjectsParams, ListProjectsResult, } from './projects.js';
4
+ export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace, } from './projects.js';
5
5
  export { projectsIntegrationEventDedup } from './schema/integration-event-dedup.js';
6
6
  export { projectsOutbox } from './schema/outbox.js';
7
7
  export declare const migrationsPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,6BAA6B,EAAC,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAElD,eAAO,MAAM,cAAc,QAAoE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAC,MAAM,SAAS,CAAC;AAC5C,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,YAAY,EACZ,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAC,6BAA6B,EAAC,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAElD,eAAO,MAAM,cAAc,QAAoE,CAAC"}
package/dist/db/index.js CHANGED
@@ -2,7 +2,7 @@ import { dirname, resolve } from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  export { closeDb, db, schema } from './db.js';
4
4
  export { pruneIntegrationEventDedup, recordIntegrationEventForProject } from './integration-event-dedup.js';
5
- export { createProject, getProjectById, getProjectBySource, getProjectCount, listProjects, requireProjectForWorkspace } from './projects.js';
5
+ export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace } from './projects.js';
6
6
  export { projectsIntegrationEventDedup } from './schema/integration-event-dedup.js';
7
7
  export { projectsOutbox } from './schema/outbox.js';
8
8
  export const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport {closeDb, db, schema} from './db.js';\nexport {\n pruneIntegrationEventDedup,\n recordIntegrationEventForProject,\n} from './integration-event-dedup.js';\nexport type {\n CreateProjectParams,\n GetProjectBySourceParams,\n ListProjectsParams,\n ListProjectsResult,\n} from './projects.js';\nexport {\n createProject,\n getProjectById,\n getProjectBySource,\n getProjectCount,\n listProjects,\n requireProjectForWorkspace,\n} from './projects.js';\nexport {projectsIntegrationEventDedup} from './schema/integration-event-dedup.js';\nexport {projectsOutbox} from './schema/outbox.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","closeDb","db","schema","pruneIntegrationEventDedup","recordIntegrationEventForProject","createProject","getProjectById","getProjectBySource","getProjectCount","listProjects","requireProjectForWorkspace","projectsIntegrationEventDedup","projectsOutbox","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAC5C,SACEC,0BAA0B,EAC1BC,gCAAgC,QAC3B,+BAA+B;AAOtC,SACEC,aAAa,EACbC,cAAc,EACdC,kBAAkB,EAClBC,eAAe,EACfC,YAAY,EACZC,0BAA0B,QACrB,gBAAgB;AACvB,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAElD,OAAO,MAAMC,iBAAiBf,QAAQD,QAAQE,cAAc,YAAYe,GAAG,IAAI,iBAAiB"}
1
+ {"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport {closeDb, db, schema} from './db.js';\nexport {\n pruneIntegrationEventDedup,\n recordIntegrationEventForProject,\n} from './integration-event-dedup.js';\nexport type {\n AdminProjectSummary,\n CreateProjectParams,\n GetProjectBySourceParams,\n ListAdminProjectsParams,\n ListAdminProjectsResult,\n ListProjectsParams,\n ListProjectsResult,\n} from './projects.js';\nexport {\n createProject,\n getProjectById,\n getProjectBySource,\n getProjectCount,\n getWorkspaceProjectCounts,\n listAdminProjects,\n listProjects,\n requireProjectForWorkspace,\n} from './projects.js';\nexport {projectsIntegrationEventDedup} from './schema/integration-event-dedup.js';\nexport {projectsOutbox} from './schema/outbox.js';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","closeDb","db","schema","pruneIntegrationEventDedup","recordIntegrationEventForProject","createProject","getProjectById","getProjectBySource","getProjectCount","getWorkspaceProjectCounts","listAdminProjects","listProjects","requireProjectForWorkspace","projectsIntegrationEventDedup","projectsOutbox","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,SAAQC,OAAO,EAAEC,EAAE,EAAEC,MAAM,QAAO,UAAU;AAC5C,SACEC,0BAA0B,EAC1BC,gCAAgC,QAC3B,+BAA+B;AAUtC,SACEC,aAAa,EACbC,cAAc,EACdC,kBAAkB,EAClBC,eAAe,EACfC,yBAAyB,EACzBC,iBAAiB,EACjBC,YAAY,EACZC,0BAA0B,QACrB,gBAAgB;AACvB,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAElD,OAAO,MAAMC,iBAAiBjB,QAAQD,QAAQE,cAAc,YAAYiB,GAAG,IAAI,iBAAiB"}
@@ -19,6 +19,17 @@ export interface ListProjectsResult {
19
19
  projects: Project[];
20
20
  nextCursor: ProjectCursor | null;
21
21
  }
22
+ export type AdminProjectSummary = Pick<Project, 'id' | 'workspaceId' | 'name' | 'createdAt' | 'updatedAt'>;
23
+ export interface ListAdminProjectsParams {
24
+ limit: number;
25
+ cursor?: ProjectCursor | undefined;
26
+ projectId?: string | undefined;
27
+ search?: string | undefined;
28
+ }
29
+ export interface ListAdminProjectsResult {
30
+ projects: AdminProjectSummary[];
31
+ nextCursor: ProjectCursor | null;
32
+ }
22
33
  export declare function createProject(params: CreateProjectParams): Promise<Project>;
23
34
  export declare function getProjectById(id: string): Promise<Project | undefined>;
24
35
  export interface GetProjectBySourceParams {
@@ -32,5 +43,12 @@ export declare function requireProjectForWorkspace(params: {
32
43
  workspaceId: string;
33
44
  }): Promise<Project>;
34
45
  export declare function listProjects(params: ListProjectsParams): Promise<ListProjectsResult>;
46
+ export declare function listAdminProjects(params: ListAdminProjectsParams): Promise<ListAdminProjectsResult>;
35
47
  export declare function getProjectCount(): Promise<number>;
48
+ export declare function getWorkspaceProjectCounts(params: {
49
+ workspaceIds: string[];
50
+ }): Promise<Array<{
51
+ workspaceId: string;
52
+ count: number;
53
+ }>>;
36
54
  //# sourceMappingURL=projects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAMvD,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0BAA0B,EAAE,MAAM,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAMD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;CAClC;AAUD,wBAAsB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwCjF;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAM7E;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAgB9B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKnB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuB1F;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAGvD"}
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,2BAA2B,CAAC;AAMvD,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0BAA0B,EAAE,MAAM,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAMD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,OAAO,EACP,IAAI,GAAG,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAC1D,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,UAAU,EAAE,aAAa,GAAG,IAAI,CAAC;CAClC;AAUD,wBAAsB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwCjF;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAM7E;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0BAA0B,EAAE,MAAM,CAAC;CACpC;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAgB9B;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKnB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuB1F;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CA8BlC;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAGvD;AAED,wBAAsB,yBAAyB,CAAC,MAAM,EAAE;IACtD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,GAAG,OAAO,CAAC,KAAK,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,CAAC,CAAC,CAYvD"}
@@ -1,4 +1,4 @@
1
- import { and, count, desc, eq, ilike, lt, or } from 'drizzle-orm';
1
+ import { and, count, desc, eq, ilike, inArray, lt, or } from 'drizzle-orm';
2
2
  import { ProjectAlreadyExistsError, ProjectNotFoundError } from '#core/errors.js';
3
3
  import { recordProjectCreated } from '#metrics/instance.js';
4
4
  import { db } from './db.js';
@@ -6,9 +6,9 @@ import { projects, toProject } from './schema/projects.js';
6
6
  function escapeIlikePattern(value) {
7
7
  return value.replace(/[\\%_]/g, (char)=>`\\${char}`);
8
8
  }
9
- function cursorWhere(params) {
10
- if (!params.cursor) return undefined;
11
- return or(lt(projects.createdAt, params.cursor.createdAt), and(eq(projects.createdAt, params.cursor.createdAt), lt(projects.id, params.cursor.id)));
9
+ function cursorWhere(cursor) {
10
+ if (!cursor) return undefined;
11
+ return or(lt(projects.createdAt, cursor.createdAt), and(eq(projects.createdAt, cursor.createdAt), lt(projects.id, cursor.id)));
12
12
  }
13
13
  export async function createProject(params) {
14
14
  const project = await db().transaction(async (tx)=>{
@@ -57,7 +57,7 @@ export async function listProjects(params) {
57
57
  const conditions = [
58
58
  eq(projects.workspaceId, params.workspaceId)
59
59
  ];
60
- const cursorCondition = cursorWhere(params);
60
+ const cursorCondition = cursorWhere(params.cursor);
61
61
  if (cursorCondition) conditions.push(cursorCondition);
62
62
  if (params.search) {
63
63
  conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));
@@ -74,11 +74,51 @@ export async function listProjects(params) {
74
74
  } : null
75
75
  };
76
76
  }
77
+ export async function listAdminProjects(params) {
78
+ const conditions = [];
79
+ const cursorCondition = cursorWhere(params.cursor);
80
+ if (cursorCondition) conditions.push(cursorCondition);
81
+ if (params.projectId) conditions.push(eq(projects.id, params.projectId));
82
+ if (params.search) {
83
+ conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));
84
+ }
85
+ const rows = await db().select({
86
+ id: projects.id,
87
+ workspaceId: projects.workspaceId,
88
+ name: projects.name,
89
+ createdAt: projects.createdAt,
90
+ updatedAt: projects.updatedAt
91
+ }).from(projects).where(conditions.length > 0 ? and(...conditions) : undefined).orderBy(desc(projects.createdAt), desc(projects.id)).limit(params.limit + 1);
92
+ const hasMore = rows.length > params.limit;
93
+ const pageRows = hasMore ? rows.slice(0, params.limit) : rows;
94
+ const last = pageRows.at(-1);
95
+ return {
96
+ projects: pageRows,
97
+ nextCursor: hasMore && last ? {
98
+ createdAt: last.createdAt,
99
+ id: last.id
100
+ } : null
101
+ };
102
+ }
77
103
  export async function getProjectCount() {
78
104
  const [row] = await db().select({
79
105
  value: count()
80
106
  }).from(projects);
81
107
  return row?.value ?? 0;
82
108
  }
109
+ export async function getWorkspaceProjectCounts(params) {
110
+ const rows = await db().select({
111
+ workspaceId: projects.workspaceId,
112
+ count: count()
113
+ }).from(projects).where(inArray(projects.workspaceId, params.workspaceIds)).groupBy(projects.workspaceId);
114
+ const countsByWorkspace = new Map(rows.map((row)=>[
115
+ row.workspaceId,
116
+ Number(row.count)
117
+ ]));
118
+ return params.workspaceIds.map((workspaceId)=>({
119
+ workspaceId,
120
+ count: countsByWorkspace.get(workspaceId) ?? 0
121
+ }));
122
+ }
83
123
 
84
124
  //# sourceMappingURL=projects.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/projects.ts"],"sourcesContent":["import {and, count, desc, eq, ilike, lt, or, type SQL} from 'drizzle-orm';\nimport type {Project} from '#core/entities/project.js';\nimport {ProjectAlreadyExistsError, ProjectNotFoundError} from '#core/errors.js';\nimport {recordProjectCreated} from '#metrics/instance.js';\nimport {db} from './db.js';\nimport {projects, toProject} from './schema/projects.js';\n\nexport interface CreateProjectParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n name: string;\n}\n\nexport interface ProjectCursor {\n createdAt: Date;\n id: string;\n}\n\nexport interface ListProjectsParams {\n workspaceId: string;\n limit: number;\n cursor?: ProjectCursor | undefined;\n search?: string | undefined;\n}\n\nfunction escapeIlikePattern(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nexport interface ListProjectsResult {\n projects: Project[];\n nextCursor: ProjectCursor | null;\n}\n\nfunction cursorWhere(params: ListProjectsParams): SQL | undefined {\n if (!params.cursor) return undefined;\n return or(\n lt(projects.createdAt, params.cursor.createdAt),\n and(eq(projects.createdAt, params.cursor.createdAt), lt(projects.id, params.cursor.id)),\n );\n}\n\nexport async function createProject(params: CreateProjectParams): Promise<Project> {\n const project = await db().transaction(async (tx) => {\n const [projectRow] = await tx\n .insert(projects)\n .values({\n workspaceId: params.workspaceId,\n sourceConnectionId: params.sourceConnectionId,\n sourceExternalRepositoryId: params.sourceExternalRepositoryId,\n name: params.name,\n })\n .onConflictDoNothing({\n target: [projects.sourceConnectionId, projects.sourceExternalRepositoryId],\n })\n .returning();\n\n if (!projectRow) {\n const [conflict] = await tx\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n if (conflict) {\n throw new ProjectAlreadyExistsError(\n conflict.id,\n params.sourceConnectionId,\n params.sourceExternalRepositoryId,\n );\n }\n throw new Error('Insert returned no rows');\n }\n\n return toProject(projectRow);\n });\n recordProjectCreated();\n return project;\n}\n\nexport async function getProjectById(id: string): Promise<Project | undefined> {\n const rows = await db().select().from(projects).where(eq(projects.id, id)).limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport interface GetProjectBySourceParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n}\n\nexport async function getProjectBySource(\n params: GetProjectBySourceParams,\n): Promise<Project | undefined> {\n const rows = await db()\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport async function requireProjectForWorkspace(params: {\n projectId: string;\n workspaceId: string;\n}): Promise<Project> {\n const project = await getProjectById(params.projectId);\n if (!project) throw new ProjectNotFoundError(params.projectId);\n if (project.workspaceId !== params.workspaceId) throw new ProjectNotFoundError(params.projectId);\n return project;\n}\n\nexport async function listProjects(params: ListProjectsParams): Promise<ListProjectsResult> {\n const conditions = [eq(projects.workspaceId, params.workspaceId)];\n const cursorCondition = cursorWhere(params);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.search) {\n conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));\n }\n\n const rows = await db()\n .select()\n .from(projects)\n .where(and(...conditions))\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows.map(toProject),\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function getProjectCount(): Promise<number> {\n const [row] = await db().select({value: count()}).from(projects);\n return row?.value ?? 0;\n}\n"],"names":["and","count","desc","eq","ilike","lt","or","ProjectAlreadyExistsError","ProjectNotFoundError","recordProjectCreated","db","projects","toProject","escapeIlikePattern","value","replace","char","cursorWhere","params","cursor","undefined","createdAt","id","createProject","project","transaction","tx","projectRow","insert","values","workspaceId","sourceConnectionId","sourceExternalRepositoryId","name","onConflictDoNothing","target","returning","conflict","select","from","where","limit","Error","getProjectById","rows","row","getProjectBySource","requireProjectForWorkspace","projectId","listProjects","conditions","cursorCondition","push","search","orderBy","hasMore","length","pageRows","slice","last","at","map","nextCursor","getProjectCount"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/db/projects.ts"],"sourcesContent":["import {and, count, desc, eq, ilike, inArray, lt, or, type SQL} from 'drizzle-orm';\nimport type {Project} from '#core/entities/project.js';\nimport {ProjectAlreadyExistsError, ProjectNotFoundError} from '#core/errors.js';\nimport {recordProjectCreated} from '#metrics/instance.js';\nimport {db} from './db.js';\nimport {projects, toProject} from './schema/projects.js';\n\nexport interface CreateProjectParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n name: string;\n}\n\nexport interface ProjectCursor {\n createdAt: Date;\n id: string;\n}\n\nexport interface ListProjectsParams {\n workspaceId: string;\n limit: number;\n cursor?: ProjectCursor | undefined;\n search?: string | undefined;\n}\n\nfunction escapeIlikePattern(value: string): string {\n return value.replace(/[\\\\%_]/g, (char) => `\\\\${char}`);\n}\n\nexport interface ListProjectsResult {\n projects: Project[];\n nextCursor: ProjectCursor | null;\n}\n\nexport type AdminProjectSummary = Pick<\n Project,\n 'id' | 'workspaceId' | 'name' | 'createdAt' | 'updatedAt'\n>;\n\nexport interface ListAdminProjectsParams {\n limit: number;\n cursor?: ProjectCursor | undefined;\n projectId?: string | undefined;\n search?: string | undefined;\n}\n\nexport interface ListAdminProjectsResult {\n projects: AdminProjectSummary[];\n nextCursor: ProjectCursor | null;\n}\n\nfunction cursorWhere(cursor: ProjectCursor | undefined): SQL | undefined {\n if (!cursor) return undefined;\n return or(\n lt(projects.createdAt, cursor.createdAt),\n and(eq(projects.createdAt, cursor.createdAt), lt(projects.id, cursor.id)),\n );\n}\n\nexport async function createProject(params: CreateProjectParams): Promise<Project> {\n const project = await db().transaction(async (tx) => {\n const [projectRow] = await tx\n .insert(projects)\n .values({\n workspaceId: params.workspaceId,\n sourceConnectionId: params.sourceConnectionId,\n sourceExternalRepositoryId: params.sourceExternalRepositoryId,\n name: params.name,\n })\n .onConflictDoNothing({\n target: [projects.sourceConnectionId, projects.sourceExternalRepositoryId],\n })\n .returning();\n\n if (!projectRow) {\n const [conflict] = await tx\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n if (conflict) {\n throw new ProjectAlreadyExistsError(\n conflict.id,\n params.sourceConnectionId,\n params.sourceExternalRepositoryId,\n );\n }\n throw new Error('Insert returned no rows');\n }\n\n return toProject(projectRow);\n });\n recordProjectCreated();\n return project;\n}\n\nexport async function getProjectById(id: string): Promise<Project | undefined> {\n const rows = await db().select().from(projects).where(eq(projects.id, id)).limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport interface GetProjectBySourceParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n}\n\nexport async function getProjectBySource(\n params: GetProjectBySourceParams,\n): Promise<Project | undefined> {\n const rows = await db()\n .select()\n .from(projects)\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, params.sourceConnectionId),\n eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toProject(row);\n}\n\nexport async function requireProjectForWorkspace(params: {\n projectId: string;\n workspaceId: string;\n}): Promise<Project> {\n const project = await getProjectById(params.projectId);\n if (!project) throw new ProjectNotFoundError(params.projectId);\n if (project.workspaceId !== params.workspaceId) throw new ProjectNotFoundError(params.projectId);\n return project;\n}\n\nexport async function listProjects(params: ListProjectsParams): Promise<ListProjectsResult> {\n const conditions = [eq(projects.workspaceId, params.workspaceId)];\n const cursorCondition = cursorWhere(params.cursor);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.search) {\n conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));\n }\n\n const rows = await db()\n .select()\n .from(projects)\n .where(and(...conditions))\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows.map(toProject),\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function listAdminProjects(\n params: ListAdminProjectsParams,\n): Promise<ListAdminProjectsResult> {\n const conditions: SQL[] = [];\n const cursorCondition = cursorWhere(params.cursor);\n if (cursorCondition) conditions.push(cursorCondition);\n if (params.projectId) conditions.push(eq(projects.id, params.projectId));\n if (params.search) {\n conditions.push(ilike(projects.name, `%${escapeIlikePattern(params.search)}%`));\n }\n\n const rows = await db()\n .select({\n id: projects.id,\n workspaceId: projects.workspaceId,\n name: projects.name,\n createdAt: projects.createdAt,\n updatedAt: projects.updatedAt,\n })\n .from(projects)\n .where(conditions.length > 0 ? and(...conditions) : undefined)\n .orderBy(desc(projects.createdAt), desc(projects.id))\n .limit(params.limit + 1);\n\n const hasMore = rows.length > params.limit;\n const pageRows = hasMore ? rows.slice(0, params.limit) : rows;\n const last = pageRows.at(-1);\n\n return {\n projects: pageRows,\n nextCursor: hasMore && last ? {createdAt: last.createdAt, id: last.id} : null,\n };\n}\n\nexport async function getProjectCount(): Promise<number> {\n const [row] = await db().select({value: count()}).from(projects);\n return row?.value ?? 0;\n}\n\nexport async function getWorkspaceProjectCounts(params: {\n workspaceIds: string[];\n}): Promise<Array<{workspaceId: string; count: number}>> {\n const rows = await db()\n .select({workspaceId: projects.workspaceId, count: count()})\n .from(projects)\n .where(inArray(projects.workspaceId, params.workspaceIds))\n .groupBy(projects.workspaceId);\n\n const countsByWorkspace = new Map(rows.map((row) => [row.workspaceId, Number(row.count)]));\n return params.workspaceIds.map((workspaceId) => ({\n workspaceId,\n count: countsByWorkspace.get(workspaceId) ?? 0,\n }));\n}\n"],"names":["and","count","desc","eq","ilike","inArray","lt","or","ProjectAlreadyExistsError","ProjectNotFoundError","recordProjectCreated","db","projects","toProject","escapeIlikePattern","value","replace","char","cursorWhere","cursor","undefined","createdAt","id","createProject","params","project","transaction","tx","projectRow","insert","values","workspaceId","sourceConnectionId","sourceExternalRepositoryId","name","onConflictDoNothing","target","returning","conflict","select","from","where","limit","Error","getProjectById","rows","row","getProjectBySource","requireProjectForWorkspace","projectId","listProjects","conditions","cursorCondition","push","search","orderBy","hasMore","length","pageRows","slice","last","at","map","nextCursor","listAdminProjects","updatedAt","getProjectCount","getWorkspaceProjectCounts","workspaceIds","groupBy","countsByWorkspace","Map","Number","get"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
+ import type { AuthInterModuleClient } from '@shipfox/api-auth-dto/inter-module';
1
2
  import type { IntegrationsModuleClient } from '@shipfox/api-integration-core-dto/inter-module';
2
3
  import { type ShipfoxModule } from '@shipfox/node-module';
3
4
  export type { Project } from '#core/index.js';
4
5
  export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError, } from '#core/index.js';
5
6
  export type { GetProjectBySourceParams } from '#db/index.js';
6
- export { createProject, db, getProjectById, getProjectBySource, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace, } from '#db/index.js';
7
+ export { createProject, db, getProjectById, getProjectBySource, getWorkspaceProjectCounts, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace, } from '#db/index.js';
7
8
  export { createProjectRoutes, requireProjectAccess } from '#presentation/index.js';
8
9
  export interface CreateProjectsModuleOptions {
9
10
  integrations: IntegrationsModuleClient;
11
+ auth: AuthInterModuleClient;
10
12
  }
11
- export declare function createProjectsModule({ integrations }: CreateProjectsModuleOptions): ShipfoxModule;
13
+ export declare function createProjectsModule({ integrations, auth, }: CreateProjectsModuleOptions): ShipfoxModule;
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,gDAAgD,CAAC;AAE7F,OAAO,EAAC,KAAK,aAAa,EAAoB,MAAM,sBAAsB,CAAC;AAe3E,YAAY,EAAC,OAAO,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAC,wBAAwB,EAAC,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,aAAa,EACb,EAAE,EACF,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAEjF,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,wBAAwB,CAAC;CACxC;AAED,wBAAgB,oBAAoB,CAAC,EAAC,YAAY,EAAC,EAAE,2BAA2B,GAAG,aAAa,CAyB/F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,oCAAoC,CAAC;AAK9E,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,gDAAgD,CAAC;AAE7F,OAAO,EAAC,KAAK,aAAa,EAAoB,MAAM,sBAAsB,CAAC;AAe3E,YAAY,EAAC,OAAO,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAC,wBAAwB,EAAC,MAAM,cAAc,CAAC;AAC3D,OAAO,EACL,aAAa,EACb,EAAE,EACF,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EACzB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,0BAA0B,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,mBAAmB,EAAE,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAEjF,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,wBAAwB,CAAC;IACvC,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAED,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,IAAI,GACL,EAAE,2BAA2B,GAAG,aAAa,CAyB7C"}
package/dist/index.js CHANGED
@@ -15,9 +15,9 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
15
15
  const maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');
16
16
  const subscriber = subscriberFactory();
17
17
  export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError } from '#core/index.js';
18
- export { createProject, db, getProjectById, getProjectBySource, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace } from '#db/index.js';
18
+ export { createProject, db, getProjectById, getProjectBySource, getWorkspaceProjectCounts, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace } from '#db/index.js';
19
19
  export { createProjectRoutes, requireProjectAccess } from '#presentation/index.js';
20
- export function createProjectsModule({ integrations }) {
20
+ export function createProjectsModule({ integrations, auth }) {
21
21
  return {
22
22
  name: 'projects',
23
23
  database: {
@@ -25,7 +25,7 @@ export function createProjectsModule({ integrations }) {
25
25
  migrationsPath,
26
26
  databaseNamespace: 'projects'
27
27
  },
28
- routes: createProjectRoutes(integrations),
28
+ routes: createProjectRoutes(integrations, auth),
29
29
  e2eRoutes: [
30
30
  projectsE2eRoutes
31
31
  ],
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport {\n INTEGRATION_SOURCE_COMMIT_PUSHED,\n type IntegrationsEventMap,\n} from '@shipfox/api-integration-core-dto';\nimport type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';\nimport {projectsEventSchemas} from '@shipfox/api-projects-dto';\nimport {type ShipfoxModule, subscriberFactory} from '@shipfox/node-module';\nimport {db, migrationsPath, projectsOutbox} from '#db/index.js';\nimport {registerProjectsServiceMetrics} from '#metrics/index.js';\nimport {projectsE2eRoutes} from '#presentation/e2eRoutes/index.js';\nimport {createProjectRoutes} from '#presentation/index.js';\nimport {createProjectsInterModulePresentation} from '#presentation/inter-module.js';\nimport {onSourceCommitPushed} from '#presentation/subscribers/index.js';\nimport {createProjectsMaintenanceActivities} from '#temporal/activities/index.js';\nimport {PROJECTS_MAINTENANCE_TASK_QUEUE} from '#temporal/constants.js';\n\nconst packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');\nconst maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');\n\nconst subscriber = subscriberFactory<IntegrationsEventMap>();\n\nexport type {Project} from '#core/index.js';\nexport {\n createProjectFromSource,\n ProjectAccessDeniedError,\n ProjectAlreadyExistsError,\n ProjectNotFoundError,\n} from '#core/index.js';\nexport type {GetProjectBySourceParams} from '#db/index.js';\nexport {\n createProject,\n db,\n getProjectById,\n getProjectBySource,\n listProjects,\n migrationsPath,\n projectsOutbox,\n requireProjectForWorkspace,\n} from '#db/index.js';\nexport {createProjectRoutes, requireProjectAccess} from '#presentation/index.js';\n\nexport interface CreateProjectsModuleOptions {\n integrations: IntegrationsModuleClient;\n}\n\nexport function createProjectsModule({integrations}: CreateProjectsModuleOptions): ShipfoxModule {\n return {\n name: 'projects',\n database: {db, migrationsPath, databaseNamespace: 'projects'},\n routes: createProjectRoutes(integrations),\n e2eRoutes: [projectsE2eRoutes],\n metrics: registerProjectsServiceMetrics,\n publishers: [{name: 'projects', table: projectsOutbox, db, eventSchemas: projectsEventSchemas}],\n interModulePresentations: [createProjectsInterModulePresentation()],\n subscribers: [subscriber(INTEGRATION_SOURCE_COMMIT_PUSHED, onSourceCommitPushed)],\n workers: [\n {\n taskQueue: PROJECTS_MAINTENANCE_TASK_QUEUE,\n workflowsPath: maintenanceWorkflowsPath,\n activities: createProjectsMaintenanceActivities,\n workflows: [\n {\n name: 'pruneIntegrationEventDedupCron',\n id: 'projects-prune-integration-event-dedup',\n cronSchedule: '0 3 * * *',\n },\n ],\n },\n ],\n };\n}\n"],"names":["dirname","resolve","fileURLToPath","INTEGRATION_SOURCE_COMMIT_PUSHED","projectsEventSchemas","subscriberFactory","db","migrationsPath","projectsOutbox","registerProjectsServiceMetrics","projectsE2eRoutes","createProjectRoutes","createProjectsInterModulePresentation","onSourceCommitPushed","createProjectsMaintenanceActivities","PROJECTS_MAINTENANCE_TASK_QUEUE","packageRoot","url","maintenanceWorkflowsPath","subscriber","createProjectFromSource","ProjectAccessDeniedError","ProjectAlreadyExistsError","ProjectNotFoundError","createProject","getProjectById","getProjectBySource","listProjects","requireProjectForWorkspace","requireProjectAccess","createProjectsModule","integrations","name","database","databaseNamespace","routes","e2eRoutes","metrics","publishers","table","eventSchemas","interModulePresentations","subscribers","workers","taskQueue","workflowsPath","activities","workflows","id","cronSchedule"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AACvC,SACEC,gCAAgC,QAE3B,oCAAoC;AAE3C,SAAQC,oBAAoB,QAAO,4BAA4B;AAC/D,SAA4BC,iBAAiB,QAAO,uBAAuB;AAC3E,SAAQC,EAAE,EAAEC,cAAc,EAAEC,cAAc,QAAO,eAAe;AAChE,SAAQC,8BAA8B,QAAO,oBAAoB;AACjE,SAAQC,iBAAiB,QAAO,mCAAmC;AACnE,SAAQC,mBAAmB,QAAO,yBAAyB;AAC3D,SAAQC,qCAAqC,QAAO,gCAAgC;AACpF,SAAQC,oBAAoB,QAAO,qCAAqC;AACxE,SAAQC,mCAAmC,QAAO,gCAAgC;AAClF,SAAQC,+BAA+B,QAAO,yBAAyB;AAEvE,MAAMC,cAAcf,QAAQD,QAAQE,cAAc,YAAYe,GAAG,IAAI;AACrE,MAAMC,2BAA2BjB,QAAQe,aAAa;AAEtD,MAAMG,aAAad;AAGnB,SACEe,uBAAuB,EACvBC,wBAAwB,EACxBC,yBAAyB,EACzBC,oBAAoB,QACf,iBAAiB;AAExB,SACEC,aAAa,EACblB,EAAE,EACFmB,cAAc,EACdC,kBAAkB,EAClBC,YAAY,EACZpB,cAAc,EACdC,cAAc,EACdoB,0BAA0B,QACrB,eAAe;AACtB,SAAQjB,mBAAmB,EAAEkB,oBAAoB,QAAO,yBAAyB;AAMjF,OAAO,SAASC,qBAAqB,EAACC,YAAY,EAA8B;IAC9E,OAAO;QACLC,MAAM;QACNC,UAAU;YAAC3B;YAAIC;YAAgB2B,mBAAmB;QAAU;QAC5DC,QAAQxB,oBAAoBoB;QAC5BK,WAAW;YAAC1B;SAAkB;QAC9B2B,SAAS5B;QACT6B,YAAY;YAAC;gBAACN,MAAM;gBAAYO,OAAO/B;gBAAgBF;gBAAIkC,cAAcpC;YAAoB;SAAE;QAC/FqC,0BAA0B;YAAC7B;SAAwC;QACnE8B,aAAa;YAACvB,WAAWhB,kCAAkCU;SAAsB;QACjF8B,SAAS;YACP;gBACEC,WAAW7B;gBACX8B,eAAe3B;gBACf4B,YAAYhC;gBACZiC,WAAW;oBACT;wBACEf,MAAM;wBACNgB,IAAI;wBACJC,cAAc;oBAChB;iBACD;YACH;SACD;IACH;AACF"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport {\n INTEGRATION_SOURCE_COMMIT_PUSHED,\n type IntegrationsEventMap,\n} from '@shipfox/api-integration-core-dto';\nimport type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';\nimport {projectsEventSchemas} from '@shipfox/api-projects-dto';\nimport {type ShipfoxModule, subscriberFactory} from '@shipfox/node-module';\nimport {db, migrationsPath, projectsOutbox} from '#db/index.js';\nimport {registerProjectsServiceMetrics} from '#metrics/index.js';\nimport {projectsE2eRoutes} from '#presentation/e2eRoutes/index.js';\nimport {createProjectRoutes} from '#presentation/index.js';\nimport {createProjectsInterModulePresentation} from '#presentation/inter-module.js';\nimport {onSourceCommitPushed} from '#presentation/subscribers/index.js';\nimport {createProjectsMaintenanceActivities} from '#temporal/activities/index.js';\nimport {PROJECTS_MAINTENANCE_TASK_QUEUE} from '#temporal/constants.js';\n\nconst packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');\nconst maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');\n\nconst subscriber = subscriberFactory<IntegrationsEventMap>();\n\nexport type {Project} from '#core/index.js';\nexport {\n createProjectFromSource,\n ProjectAccessDeniedError,\n ProjectAlreadyExistsError,\n ProjectNotFoundError,\n} from '#core/index.js';\nexport type {GetProjectBySourceParams} from '#db/index.js';\nexport {\n createProject,\n db,\n getProjectById,\n getProjectBySource,\n getWorkspaceProjectCounts,\n listProjects,\n migrationsPath,\n projectsOutbox,\n requireProjectForWorkspace,\n} from '#db/index.js';\nexport {createProjectRoutes, requireProjectAccess} from '#presentation/index.js';\n\nexport interface CreateProjectsModuleOptions {\n integrations: IntegrationsModuleClient;\n auth: AuthInterModuleClient;\n}\n\nexport function createProjectsModule({\n integrations,\n auth,\n}: CreateProjectsModuleOptions): ShipfoxModule {\n return {\n name: 'projects',\n database: {db, migrationsPath, databaseNamespace: 'projects'},\n routes: createProjectRoutes(integrations, auth),\n e2eRoutes: [projectsE2eRoutes],\n metrics: registerProjectsServiceMetrics,\n publishers: [{name: 'projects', table: projectsOutbox, db, eventSchemas: projectsEventSchemas}],\n interModulePresentations: [createProjectsInterModulePresentation()],\n subscribers: [subscriber(INTEGRATION_SOURCE_COMMIT_PUSHED, onSourceCommitPushed)],\n workers: [\n {\n taskQueue: PROJECTS_MAINTENANCE_TASK_QUEUE,\n workflowsPath: maintenanceWorkflowsPath,\n activities: createProjectsMaintenanceActivities,\n workflows: [\n {\n name: 'pruneIntegrationEventDedupCron',\n id: 'projects-prune-integration-event-dedup',\n cronSchedule: '0 3 * * *',\n },\n ],\n },\n ],\n };\n}\n"],"names":["dirname","resolve","fileURLToPath","INTEGRATION_SOURCE_COMMIT_PUSHED","projectsEventSchemas","subscriberFactory","db","migrationsPath","projectsOutbox","registerProjectsServiceMetrics","projectsE2eRoutes","createProjectRoutes","createProjectsInterModulePresentation","onSourceCommitPushed","createProjectsMaintenanceActivities","PROJECTS_MAINTENANCE_TASK_QUEUE","packageRoot","url","maintenanceWorkflowsPath","subscriber","createProjectFromSource","ProjectAccessDeniedError","ProjectAlreadyExistsError","ProjectNotFoundError","createProject","getProjectById","getProjectBySource","getWorkspaceProjectCounts","listProjects","requireProjectForWorkspace","requireProjectAccess","createProjectsModule","integrations","auth","name","database","databaseNamespace","routes","e2eRoutes","metrics","publishers","table","eventSchemas","interModulePresentations","subscribers","workers","taskQueue","workflowsPath","activities","workflows","id","cronSchedule"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,SACEC,gCAAgC,QAE3B,oCAAoC;AAE3C,SAAQC,oBAAoB,QAAO,4BAA4B;AAC/D,SAA4BC,iBAAiB,QAAO,uBAAuB;AAC3E,SAAQC,EAAE,EAAEC,cAAc,EAAEC,cAAc,QAAO,eAAe;AAChE,SAAQC,8BAA8B,QAAO,oBAAoB;AACjE,SAAQC,iBAAiB,QAAO,mCAAmC;AACnE,SAAQC,mBAAmB,QAAO,yBAAyB;AAC3D,SAAQC,qCAAqC,QAAO,gCAAgC;AACpF,SAAQC,oBAAoB,QAAO,qCAAqC;AACxE,SAAQC,mCAAmC,QAAO,gCAAgC;AAClF,SAAQC,+BAA+B,QAAO,yBAAyB;AAEvE,MAAMC,cAAcf,QAAQD,QAAQE,cAAc,YAAYe,GAAG,IAAI;AACrE,MAAMC,2BAA2BjB,QAAQe,aAAa;AAEtD,MAAMG,aAAad;AAGnB,SACEe,uBAAuB,EACvBC,wBAAwB,EACxBC,yBAAyB,EACzBC,oBAAoB,QACf,iBAAiB;AAExB,SACEC,aAAa,EACblB,EAAE,EACFmB,cAAc,EACdC,kBAAkB,EAClBC,yBAAyB,EACzBC,YAAY,EACZrB,cAAc,EACdC,cAAc,EACdqB,0BAA0B,QACrB,eAAe;AACtB,SAAQlB,mBAAmB,EAAEmB,oBAAoB,QAAO,yBAAyB;AAOjF,OAAO,SAASC,qBAAqB,EACnCC,YAAY,EACZC,IAAI,EACwB;IAC5B,OAAO;QACLC,MAAM;QACNC,UAAU;YAAC7B;YAAIC;YAAgB6B,mBAAmB;QAAU;QAC5DC,QAAQ1B,oBAAoBqB,cAAcC;QAC1CK,WAAW;YAAC5B;SAAkB;QAC9B6B,SAAS9B;QACT+B,YAAY;YAAC;gBAACN,MAAM;gBAAYO,OAAOjC;gBAAgBF;gBAAIoC,cAActC;YAAoB;SAAE;QAC/FuC,0BAA0B;YAAC/B;SAAwC;QACnEgC,aAAa;YAACzB,WAAWhB,kCAAkCU;SAAsB;QACjFgC,SAAS;YACP;gBACEC,WAAW/B;gBACXgC,eAAe7B;gBACf8B,YAAYlC;gBACZmC,WAAW;oBACT;wBACEf,MAAM;wBACNgB,IAAI;wBACJC,cAAc;oBAChB;iBACD;YACH;SACD;IACH;AACF"}
@@ -1,2 +1,2 @@
1
- export { toProjectDto } from './project.js';
1
+ export { toAdminProjectSummaryDto, toProjectDto } from './project.js';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,wBAAwB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC"}
@@ -1,3 +1,3 @@
1
- export { toProjectDto } from './project.js';
1
+ export { toAdminProjectSummaryDto, toProjectDto } from './project.js';
2
2
 
3
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/index.ts"],"sourcesContent":["export {toProjectDto} from './project.js';\n"],"names":["toProjectDto"],"mappings":"AAAA,SAAQA,YAAY,QAAO,eAAe"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/index.ts"],"sourcesContent":["export {toAdminProjectSummaryDto, toProjectDto} from './project.js';\n"],"names":["toAdminProjectSummaryDto","toProjectDto"],"mappings":"AAAA,SAAQA,wBAAwB,EAAEC,YAAY,QAAO,eAAe"}
@@ -1,4 +1,5 @@
1
1
  import type { Project } from '#core/entities/index.js';
2
+ import type { AdminProjectSummary } from '#db/projects.js';
2
3
  export declare function toProjectDto(project: Project): {
3
4
  id: string;
4
5
  workspace_id: string;
@@ -10,4 +11,12 @@ export declare function toProjectDto(project: Project): {
10
11
  created_at: string;
11
12
  updated_at: string;
12
13
  };
14
+ export declare function toAdminProjectSummaryDto(project: AdminProjectSummary): {
15
+ id: string;
16
+ name: string;
17
+ status: "active";
18
+ workspace_id: string;
19
+ created_at: string;
20
+ updated_at: string;
21
+ };
13
22
  //# sourceMappingURL=project.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,yBAAyB,CAAC;AAErD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;EAY5C"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAEzD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;EAY5C;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,mBAAmB;;;;;;;EASpE"}
@@ -11,5 +11,15 @@ export function toProjectDto(project) {
11
11
  updated_at: project.updatedAt.toISOString()
12
12
  };
13
13
  }
14
+ export function toAdminProjectSummaryDto(project) {
15
+ return {
16
+ id: project.id,
17
+ name: project.name,
18
+ status: 'active',
19
+ workspace_id: project.workspaceId,
20
+ created_at: project.createdAt.toISOString(),
21
+ updated_at: project.updatedAt.toISOString()
22
+ };
23
+ }
14
24
 
15
25
  //# sourceMappingURL=project.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/dto/project.ts"],"sourcesContent":["import type {Project} from '#core/entities/index.js';\n\nexport function toProjectDto(project: Project) {\n return {\n id: project.id,\n workspace_id: project.workspaceId,\n name: project.name,\n source: {\n connection_id: project.sourceConnectionId,\n external_repository_id: project.sourceExternalRepositoryId,\n },\n created_at: project.createdAt.toISOString(),\n updated_at: project.updatedAt.toISOString(),\n };\n}\n"],"names":["toProjectDto","project","id","workspace_id","workspaceId","name","source","connection_id","sourceConnectionId","external_repository_id","sourceExternalRepositoryId","created_at","createdAt","toISOString","updated_at","updatedAt"],"mappings":"AAEA,OAAO,SAASA,aAAaC,OAAgB;IAC3C,OAAO;QACLC,IAAID,QAAQC,EAAE;QACdC,cAAcF,QAAQG,WAAW;QACjCC,MAAMJ,QAAQI,IAAI;QAClBC,QAAQ;YACNC,eAAeN,QAAQO,kBAAkB;YACzCC,wBAAwBR,QAAQS,0BAA0B;QAC5D;QACAC,YAAYV,QAAQW,SAAS,CAACC,WAAW;QACzCC,YAAYb,QAAQc,SAAS,CAACF,WAAW;IAC3C;AACF"}
1
+ {"version":3,"sources":["../../../src/presentation/dto/project.ts"],"sourcesContent":["import type {Project} from '#core/entities/index.js';\nimport type {AdminProjectSummary} from '#db/projects.js';\n\nexport function toProjectDto(project: Project) {\n return {\n id: project.id,\n workspace_id: project.workspaceId,\n name: project.name,\n source: {\n connection_id: project.sourceConnectionId,\n external_repository_id: project.sourceExternalRepositoryId,\n },\n created_at: project.createdAt.toISOString(),\n updated_at: project.updatedAt.toISOString(),\n };\n}\n\nexport function toAdminProjectSummaryDto(project: AdminProjectSummary) {\n return {\n id: project.id,\n name: project.name,\n status: 'active' as const,\n workspace_id: project.workspaceId,\n created_at: project.createdAt.toISOString(),\n updated_at: project.updatedAt.toISOString(),\n };\n}\n"],"names":["toProjectDto","project","id","workspace_id","workspaceId","name","source","connection_id","sourceConnectionId","external_repository_id","sourceExternalRepositoryId","created_at","createdAt","toISOString","updated_at","updatedAt","toAdminProjectSummaryDto","status"],"mappings":"AAGA,OAAO,SAASA,aAAaC,OAAgB;IAC3C,OAAO;QACLC,IAAID,QAAQC,EAAE;QACdC,cAAcF,QAAQG,WAAW;QACjCC,MAAMJ,QAAQI,IAAI;QAClBC,QAAQ;YACNC,eAAeN,QAAQO,kBAAkB;YACzCC,wBAAwBR,QAAQS,0BAA0B;QAC5D;QACAC,YAAYV,QAAQW,SAAS,CAACC,WAAW;QACzCC,YAAYb,QAAQc,SAAS,CAACF,WAAW;IAC3C;AACF;AAEA,OAAO,SAASG,yBAAyBf,OAA4B;IACnE,OAAO;QACLC,IAAID,QAAQC,EAAE;QACdG,MAAMJ,QAAQI,IAAI;QAClBY,QAAQ;QACRd,cAAcF,QAAQG,WAAW;QACjCO,YAAYV,QAAQW,SAAS,CAACC,WAAW;QACzCC,YAAYb,QAAQc,SAAS,CAACF,WAAW;IAC3C;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"inter-module.d.ts","sourceRoot":"","sources":["../../src/presentation/inter-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,2BAA2B,EAAC,MAAM,wCAAwC,CAAC;AACnF,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAG/B,wBAAgB,qCAAqC,IAAI,uBAAuB,CAC9E,OAAO,2BAA2B,CACnC,CAsBA"}
1
+ {"version":3,"file":"inter-module.d.ts","sourceRoot":"","sources":["../../src/presentation/inter-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,2BAA2B,EAAC,MAAM,wCAAwC,CAAC;AACnF,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAG/B,wBAAgB,qCAAqC,IAAI,uBAAuB,CAC9E,OAAO,2BAA2B,CACnC,CAyBA"}
@@ -1,6 +1,6 @@
1
1
  import { projectsInterModuleContract } from '@shipfox/api-projects-dto/inter-module';
2
2
  import { createInterModuleKnownError, defineInterModulePresentation } from '@shipfox/inter-module';
3
- import { getProjectById } from '#db/projects.js';
3
+ import { getProjectById, getWorkspaceProjectCounts } from '#db/projects.js';
4
4
  export function createProjectsInterModulePresentation() {
5
5
  return defineInterModulePresentation(projectsInterModuleContract, {
6
6
  getProjectById: async ({ projectId })=>({
@@ -22,7 +22,12 @@ export function createProjectsInterModulePresentation() {
22
22
  return {
23
23
  project
24
24
  };
25
- }
25
+ },
26
+ getWorkspaceProjectCounts: async ({ workspaceIds })=>({
27
+ counts: await getWorkspaceProjectCounts({
28
+ workspaceIds
29
+ })
30
+ })
26
31
  });
27
32
  }
28
33
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/presentation/inter-module.ts"],"sourcesContent":["import {projectsInterModuleContract} from '@shipfox/api-projects-dto/inter-module';\nimport {\n createInterModuleKnownError,\n defineInterModulePresentation,\n type InterModulePresentation,\n} from '@shipfox/inter-module';\nimport {getProjectById} from '#db/projects.js';\n\nexport function createProjectsInterModulePresentation(): InterModulePresentation<\n typeof projectsInterModuleContract\n> {\n return defineInterModulePresentation(projectsInterModuleContract, {\n getProjectById: async ({projectId}) => ({project: (await getProjectById(projectId)) ?? null}),\n requireProjectForWorkspace: async ({projectId, workspaceId}) => {\n const project = await getProjectById(projectId);\n if (project === undefined) {\n throw createInterModuleKnownError(\n projectsInterModuleContract.methods.requireProjectForWorkspace,\n 'project-not-found',\n {projectId},\n );\n }\n if (project.workspaceId !== workspaceId) {\n throw createInterModuleKnownError(\n projectsInterModuleContract.methods.requireProjectForWorkspace,\n 'project-workspace-mismatch',\n {projectId, workspaceId},\n );\n }\n return {project};\n },\n });\n}\n"],"names":["projectsInterModuleContract","createInterModuleKnownError","defineInterModulePresentation","getProjectById","createProjectsInterModulePresentation","projectId","project","requireProjectForWorkspace","workspaceId","undefined","methods"],"mappings":"AAAA,SAAQA,2BAA2B,QAAO,yCAAyC;AACnF,SACEC,2BAA2B,EAC3BC,6BAA6B,QAExB,wBAAwB;AAC/B,SAAQC,cAAc,QAAO,kBAAkB;AAE/C,OAAO,SAASC;IAGd,OAAOF,8BAA8BF,6BAA6B;QAChEG,gBAAgB,OAAO,EAACE,SAAS,EAAC,GAAM,CAAA;gBAACC,SAAS,AAAC,MAAMH,eAAeE,cAAe;YAAI,CAAA;QAC3FE,4BAA4B,OAAO,EAACF,SAAS,EAAEG,WAAW,EAAC;YACzD,MAAMF,UAAU,MAAMH,eAAeE;YACrC,IAAIC,YAAYG,WAAW;gBACzB,MAAMR,4BACJD,4BAA4BU,OAAO,CAACH,0BAA0B,EAC9D,qBACA;oBAACF;gBAAS;YAEd;YACA,IAAIC,QAAQE,WAAW,KAAKA,aAAa;gBACvC,MAAMP,4BACJD,4BAA4BU,OAAO,CAACH,0BAA0B,EAC9D,8BACA;oBAACF;oBAAWG;gBAAW;YAE3B;YACA,OAAO;gBAACF;YAAO;QACjB;IACF;AACF"}
1
+ {"version":3,"sources":["../../src/presentation/inter-module.ts"],"sourcesContent":["import {projectsInterModuleContract} from '@shipfox/api-projects-dto/inter-module';\nimport {\n createInterModuleKnownError,\n defineInterModulePresentation,\n type InterModulePresentation,\n} from '@shipfox/inter-module';\nimport {getProjectById, getWorkspaceProjectCounts} from '#db/projects.js';\n\nexport function createProjectsInterModulePresentation(): InterModulePresentation<\n typeof projectsInterModuleContract\n> {\n return defineInterModulePresentation(projectsInterModuleContract, {\n getProjectById: async ({projectId}) => ({project: (await getProjectById(projectId)) ?? null}),\n requireProjectForWorkspace: async ({projectId, workspaceId}) => {\n const project = await getProjectById(projectId);\n if (project === undefined) {\n throw createInterModuleKnownError(\n projectsInterModuleContract.methods.requireProjectForWorkspace,\n 'project-not-found',\n {projectId},\n );\n }\n if (project.workspaceId !== workspaceId) {\n throw createInterModuleKnownError(\n projectsInterModuleContract.methods.requireProjectForWorkspace,\n 'project-workspace-mismatch',\n {projectId, workspaceId},\n );\n }\n return {project};\n },\n getWorkspaceProjectCounts: async ({workspaceIds}) => ({\n counts: await getWorkspaceProjectCounts({workspaceIds}),\n }),\n });\n}\n"],"names":["projectsInterModuleContract","createInterModuleKnownError","defineInterModulePresentation","getProjectById","getWorkspaceProjectCounts","createProjectsInterModulePresentation","projectId","project","requireProjectForWorkspace","workspaceId","undefined","methods","workspaceIds","counts"],"mappings":"AAAA,SAAQA,2BAA2B,QAAO,yCAAyC;AACnF,SACEC,2BAA2B,EAC3BC,6BAA6B,QAExB,wBAAwB;AAC/B,SAAQC,cAAc,EAAEC,yBAAyB,QAAO,kBAAkB;AAE1E,OAAO,SAASC;IAGd,OAAOH,8BAA8BF,6BAA6B;QAChEG,gBAAgB,OAAO,EAACG,SAAS,EAAC,GAAM,CAAA;gBAACC,SAAS,AAAC,MAAMJ,eAAeG,cAAe;YAAI,CAAA;QAC3FE,4BAA4B,OAAO,EAACF,SAAS,EAAEG,WAAW,EAAC;YACzD,MAAMF,UAAU,MAAMJ,eAAeG;YACrC,IAAIC,YAAYG,WAAW;gBACzB,MAAMT,4BACJD,4BAA4BW,OAAO,CAACH,0BAA0B,EAC9D,qBACA;oBAACF;gBAAS;YAEd;YACA,IAAIC,QAAQE,WAAW,KAAKA,aAAa;gBACvC,MAAMR,4BACJD,4BAA4BW,OAAO,CAACH,0BAA0B,EAC9D,8BACA;oBAACF;oBAAWG;gBAAW;YAE3B;YACA,OAAO;gBAACF;YAAO;QACjB;QACAH,2BAA2B,OAAO,EAACQ,YAAY,EAAC,GAAM,CAAA;gBACpDC,QAAQ,MAAMT,0BAA0B;oBAACQ;gBAAY;YACvD,CAAA;IACF;AACF"}
@@ -0,0 +1,3 @@
1
+ import { type AuthInterModuleClient } from '@shipfox/api-auth-dto/inter-module';
2
+ export declare function createAdminProjectsRoute(auth: Pick<AuthInterModuleClient, 'requireAdminRole'>): import("@shipfox/node-fastify").RouteDefinition;
3
+ //# sourceMappingURL=admin-projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-projects.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/admin-projects.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,oCAAoC,CAAC;AA2B5C,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,mDAkC7F"}
@@ -0,0 +1,61 @@
1
+ import { AUTH_USER, requireUserContext } from '@shipfox/api-auth-context';
2
+ import { authInterModuleContract } from '@shipfox/api-auth-dto/inter-module';
3
+ import { listAdminProjectsQuerySchema, listAdminProjectsResponseSchema } from '@shipfox/api-projects-dto';
4
+ import { isInterModuleKnownError } from '@shipfox/inter-module';
5
+ import { ClientError, defineRoute } from '@shipfox/node-fastify';
6
+ import { listAdminProjects } from '#db/index.js';
7
+ import { toAdminProjectSummaryDto } from '#presentation/dto/index.js';
8
+ import { decodeProjectCursor, encodeProjectCursor } from './cursor.js';
9
+ const minimumRole = 'admin-observer';
10
+ function translateAdminProjectRouteError(error) {
11
+ if (isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) && error.code === 'admin-role-required') {
12
+ throw new ClientError('Administrator observer role required', 'forbidden', {
13
+ status: 403,
14
+ details: {
15
+ required_role: error.details.requiredRole
16
+ }
17
+ });
18
+ }
19
+ throw error;
20
+ }
21
+ export function createAdminProjectsRoute(auth) {
22
+ return defineRoute({
23
+ method: 'GET',
24
+ path: '/',
25
+ auth: AUTH_USER,
26
+ description: 'List a bounded safe project summary for administrators.',
27
+ schema: {
28
+ querystring: listAdminProjectsQuerySchema,
29
+ response: {
30
+ 200: listAdminProjectsResponseSchema
31
+ }
32
+ },
33
+ errorHandler: translateAdminProjectRouteError,
34
+ handler: async (request)=>{
35
+ const actor = requireUserContext(request);
36
+ await auth.requireAdminRole({
37
+ userId: actor.userId,
38
+ minimumRole
39
+ });
40
+ const { project_id: projectId, limit, cursor, search } = request.query;
41
+ const decodedCursor = decodeProjectCursor(cursor);
42
+ if (cursor && !decodedCursor) {
43
+ throw new ClientError('Invalid cursor', 'invalid-cursor', {
44
+ status: 400
45
+ });
46
+ }
47
+ const result = await listAdminProjects({
48
+ projectId,
49
+ limit,
50
+ cursor: decodedCursor,
51
+ search
52
+ });
53
+ return {
54
+ projects: result.projects.map(toAdminProjectSummaryDto),
55
+ next_cursor: result.nextCursor ? encodeProjectCursor(result.nextCursor) : null
56
+ };
57
+ }
58
+ });
59
+ }
60
+
61
+ //# sourceMappingURL=admin-projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/routes/admin-projects.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext} from '@shipfox/api-auth-context';\nimport {\n type AuthInterModuleClient,\n authInterModuleContract,\n} from '@shipfox/api-auth-dto/inter-module';\nimport {\n listAdminProjectsQuerySchema,\n listAdminProjectsResponseSchema,\n} from '@shipfox/api-projects-dto';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {listAdminProjects} from '#db/index.js';\nimport {toAdminProjectSummaryDto} from '#presentation/dto/index.js';\nimport {decodeProjectCursor, encodeProjectCursor} from './cursor.js';\n\nconst minimumRole = 'admin-observer' as const;\n\nfunction translateAdminProjectRouteError(error: unknown): never {\n if (\n isInterModuleKnownError(authInterModuleContract.methods.requireAdminRole, error) &&\n error.code === 'admin-role-required'\n ) {\n throw new ClientError('Administrator observer role required', 'forbidden', {\n status: 403,\n details: {required_role: error.details.requiredRole},\n });\n }\n\n throw error;\n}\n\nexport function createAdminProjectsRoute(auth: Pick<AuthInterModuleClient, 'requireAdminRole'>) {\n return defineRoute({\n method: 'GET',\n path: '/',\n auth: AUTH_USER,\n description: 'List a bounded safe project summary for administrators.',\n schema: {\n querystring: listAdminProjectsQuerySchema,\n response: {200: listAdminProjectsResponseSchema},\n },\n errorHandler: translateAdminProjectRouteError,\n handler: async (request) => {\n const actor = requireUserContext(request);\n await auth.requireAdminRole({userId: actor.userId, minimumRole});\n\n const {project_id: projectId, limit, cursor, search} = request.query;\n const decodedCursor = decodeProjectCursor(cursor);\n if (cursor && !decodedCursor) {\n throw new ClientError('Invalid cursor', 'invalid-cursor', {status: 400});\n }\n\n const result = await listAdminProjects({\n projectId,\n limit,\n cursor: decodedCursor,\n search,\n });\n\n return {\n projects: result.projects.map(toAdminProjectSummaryDto),\n next_cursor: result.nextCursor ? encodeProjectCursor(result.nextCursor) : null,\n };\n },\n });\n}\n"],"names":["AUTH_USER","requireUserContext","authInterModuleContract","listAdminProjectsQuerySchema","listAdminProjectsResponseSchema","isInterModuleKnownError","ClientError","defineRoute","listAdminProjects","toAdminProjectSummaryDto","decodeProjectCursor","encodeProjectCursor","minimumRole","translateAdminProjectRouteError","error","methods","requireAdminRole","code","status","details","required_role","requiredRole","createAdminProjectsRoute","auth","method","path","description","schema","querystring","response","errorHandler","handler","request","actor","userId","project_id","projectId","limit","cursor","search","query","decodedCursor","result","projects","map","next_cursor","nextCursor"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,kBAAkB,QAAO,4BAA4B;AACxE,SAEEC,uBAAuB,QAClB,qCAAqC;AAC5C,SACEC,4BAA4B,EAC5BC,+BAA+B,QAC1B,4BAA4B;AACnC,SAAQC,uBAAuB,QAAO,wBAAwB;AAC9D,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,iBAAiB,QAAO,eAAe;AAC/C,SAAQC,wBAAwB,QAAO,6BAA6B;AACpE,SAAQC,mBAAmB,EAAEC,mBAAmB,QAAO,cAAc;AAErE,MAAMC,cAAc;AAEpB,SAASC,gCAAgCC,KAAc;IACrD,IACET,wBAAwBH,wBAAwBa,OAAO,CAACC,gBAAgB,EAAEF,UAC1EA,MAAMG,IAAI,KAAK,uBACf;QACA,MAAM,IAAIX,YAAY,wCAAwC,aAAa;YACzEY,QAAQ;YACRC,SAAS;gBAACC,eAAeN,MAAMK,OAAO,CAACE,YAAY;YAAA;QACrD;IACF;IAEA,MAAMP;AACR;AAEA,OAAO,SAASQ,yBAAyBC,IAAqD;IAC5F,OAAOhB,YAAY;QACjBiB,QAAQ;QACRC,MAAM;QACNF,MAAMvB;QACN0B,aAAa;QACbC,QAAQ;YACNC,aAAazB;YACb0B,UAAU;gBAAC,KAAKzB;YAA+B;QACjD;QACA0B,cAAcjB;QACdkB,SAAS,OAAOC;YACd,MAAMC,QAAQhC,mBAAmB+B;YACjC,MAAMT,KAAKP,gBAAgB,CAAC;gBAACkB,QAAQD,MAAMC,MAAM;gBAAEtB;YAAW;YAE9D,MAAM,EAACuB,YAAYC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAC,GAAGP,QAAQQ,KAAK;YACpE,MAAMC,gBAAgB/B,oBAAoB4B;YAC1C,IAAIA,UAAU,CAACG,eAAe;gBAC5B,MAAM,IAAInC,YAAY,kBAAkB,kBAAkB;oBAACY,QAAQ;gBAAG;YACxE;YAEA,MAAMwB,SAAS,MAAMlC,kBAAkB;gBACrC4B;gBACAC;gBACAC,QAAQG;gBACRF;YACF;YAEA,OAAO;gBACLI,UAAUD,OAAOC,QAAQ,CAACC,GAAG,CAACnC;gBAC9BoC,aAAaH,OAAOI,UAAU,GAAGnC,oBAAoB+B,OAAOI,UAAU,IAAI;YAC5E;QACF;IACF;AACF"}
@@ -1,4 +1,5 @@
1
+ import type { AuthInterModuleClient } from '@shipfox/api-auth-dto/inter-module';
1
2
  import type { IntegrationsModuleClient } from '@shipfox/api-integration-core-dto/inter-module';
2
3
  import type { RouteGroup } from '@shipfox/node-fastify';
3
- export declare function createProjectRoutes(integrations: IntegrationsModuleClient): RouteGroup[];
4
+ export declare function createProjectRoutes(integrations: IntegrationsModuleClient, auth: Pick<AuthInterModuleClient, 'requireAdminRole'>): RouteGroup[];
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,gDAAgD,CAAC;AAC7F,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKtD,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,wBAAwB,GAAG,UAAU,EAAE,CAOxF"}
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,wBAAwB,EAAC,MAAM,gDAAgD,CAAC;AAC7F,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAMtD,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,wBAAwB,EACtC,IAAI,EAAE,IAAI,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,GACpD,UAAU,EAAE,CAWd"}
@@ -1,7 +1,8 @@
1
+ import { createAdminProjectsRoute } from './admin-projects.js';
1
2
  import { createProjectRoute } from './create-project.js';
2
3
  import { getProjectRoute } from './get-project.js';
3
4
  import { listProjectsRoute } from './list-projects.js';
4
- export function createProjectRoutes(integrations) {
5
+ export function createProjectRoutes(integrations, auth) {
5
6
  return [
6
7
  {
7
8
  prefix: '/projects',
@@ -10,6 +11,12 @@ export function createProjectRoutes(integrations) {
10
11
  listProjectsRoute,
11
12
  getProjectRoute
12
13
  ]
14
+ },
15
+ {
16
+ prefix: '/admin/projects',
17
+ routes: [
18
+ createAdminProjectsRoute(auth)
19
+ ]
13
20
  }
14
21
  ];
15
22
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createProjectRoute} from './create-project.js';\nimport {getProjectRoute} from './get-project.js';\nimport {listProjectsRoute} from './list-projects.js';\n\nexport function createProjectRoutes(integrations: IntegrationsModuleClient): RouteGroup[] {\n return [\n {\n prefix: '/projects',\n routes: [createProjectRoute(integrations), listProjectsRoute, getProjectRoute],\n },\n ];\n}\n"],"names":["createProjectRoute","getProjectRoute","listProjectsRoute","createProjectRoutes","integrations","prefix","routes"],"mappings":"AAEA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,eAAe,QAAO,mBAAmB;AACjD,SAAQC,iBAAiB,QAAO,qBAAqB;AAErD,OAAO,SAASC,oBAAoBC,YAAsC;IACxE,OAAO;QACL;YACEC,QAAQ;YACRC,QAAQ;gBAACN,mBAAmBI;gBAAeF;gBAAmBD;aAAgB;QAChF;KACD;AACH"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import type {AuthInterModuleClient} from '@shipfox/api-auth-dto/inter-module';\nimport type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto/inter-module';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {createAdminProjectsRoute} from './admin-projects.js';\nimport {createProjectRoute} from './create-project.js';\nimport {getProjectRoute} from './get-project.js';\nimport {listProjectsRoute} from './list-projects.js';\n\nexport function createProjectRoutes(\n integrations: IntegrationsModuleClient,\n auth: Pick<AuthInterModuleClient, 'requireAdminRole'>,\n): RouteGroup[] {\n return [\n {\n prefix: '/projects',\n routes: [createProjectRoute(integrations), listProjectsRoute, getProjectRoute],\n },\n {\n prefix: '/admin/projects',\n routes: [createAdminProjectsRoute(auth)],\n },\n ];\n}\n"],"names":["createAdminProjectsRoute","createProjectRoute","getProjectRoute","listProjectsRoute","createProjectRoutes","integrations","auth","prefix","routes"],"mappings":"AAGA,SAAQA,wBAAwB,QAAO,sBAAsB;AAC7D,SAAQC,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,eAAe,QAAO,mBAAmB;AACjD,SAAQC,iBAAiB,QAAO,qBAAqB;AAErD,OAAO,SAASC,oBACdC,YAAsC,EACtCC,IAAqD;IAErD,OAAO;QACL;YACEC,QAAQ;YACRC,QAAQ;gBAACP,mBAAmBI;gBAAeF;gBAAmBD;aAAgB;QAChF;QACA;YACEK,QAAQ;YACRC,QAAQ;gBAACR,yBAAyBM;aAAM;QAC1C;KACD;AACH"}