@shipfox/api-projects 9.2.0 → 10.0.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.
@@ -1,6 +1,6 @@
1
1
  $ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
2
- Successfully compiled: 36 files with swc (362.04ms)
3
- 2026-07-26T13:33:24.361Z [INFO] asset workflow-bundle-601f98c1048b285735c6.js 3.17 MiB [emitted] [immutable] (name: main)
2
+ Successfully compiled: 36 files with swc (492.15ms)
3
+ 2026-07-28T07:35:43.658Z [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 6543 ms
16
- 2026-07-26T13:33:24.372Z [INFO] Workflow bundle created { size: '3.17MB' }
15
+ webpack 5.107.2 compiled successfully in 5728 ms
16
+ 2026-07-28T07:35:43.667Z [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,40 @@
1
1
  # @shipfox/api-projects
2
2
 
3
+ ## 10.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e9280fc: Add an observer-authorized administrator workspace lookup with bounded safe summaries,
8
+ best-effort job counts, and a neutral unavailable-workspace member experience for
9
+ suspended or deleted workspaces.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [74f9e31]
14
+ - Updated dependencies [e9280fc]
15
+ - @shipfox/node-fastify@0.4.0
16
+ - @shipfox/api-projects-dto@10.0.0
17
+ - @shipfox/api-auth-context@10.0.0
18
+ - @shipfox/node-module@1.0.4
19
+ - @shipfox/api-integration-core-dto@9.0.2
20
+ - @shipfox/inter-module@0.2.2
21
+ - @shipfox/node-drizzle@0.3.4
22
+ - @shipfox/node-opentelemetry@0.6.3
23
+ - @shipfox/node-outbox@0.2.6
24
+ - @shipfox/node-postgres@0.4.4
25
+ - @shipfox/node-temporal@0.4.4
26
+
27
+ ## 9.3.0
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [4425c6d]
32
+ - @shipfox/node-opentelemetry@0.6.3
33
+ - @shipfox/api-auth-context@9.3.0
34
+ - @shipfox/node-fastify@0.3.4
35
+ - @shipfox/node-module@1.0.3
36
+ - @shipfox/node-temporal@0.4.4
37
+
3
38
  ## 9.2.0
4
39
 
5
40
  ### 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
3
  export type { CreateProjectParams, GetProjectBySourceParams, ListProjectsParams, ListProjectsResult, } from './projects.js';
4
- export { createProject, getProjectById, getProjectBySource, getProjectCount, listProjects, requireProjectForWorkspace, } from './projects.js';
4
+ export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, 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,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,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, 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 CreateProjectParams,\n GetProjectBySourceParams,\n ListProjectsParams,\n ListProjectsResult,\n} from './projects.js';\nexport {\n createProject,\n getProjectById,\n getProjectBySource,\n getProjectCount,\n getWorkspaceProjectCounts,\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","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,yBAAyB,EACzBC,YAAY,EACZC,0BAA0B,QACrB,gBAAgB;AACvB,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAElD,OAAO,MAAMC,iBAAiBhB,QAAQD,QAAQE,cAAc,YAAYgB,GAAG,IAAI,iBAAiB"}
@@ -33,4 +33,10 @@ export declare function requireProjectForWorkspace(params: {
33
33
  }): Promise<Project>;
34
34
  export declare function listProjects(params: ListProjectsParams): Promise<ListProjectsResult>;
35
35
  export declare function getProjectCount(): Promise<number>;
36
+ export declare function getWorkspaceProjectCounts(params: {
37
+ workspaceIds: string[];
38
+ }): Promise<Array<{
39
+ workspaceId: string;
40
+ count: number;
41
+ }>>;
36
42
  //# 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;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;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';
@@ -80,5 +80,19 @@ export async function getProjectCount() {
80
80
  }).from(projects);
81
81
  return row?.value ?? 0;
82
82
  }
83
+ export async function getWorkspaceProjectCounts(params) {
84
+ const rows = await db().select({
85
+ workspaceId: projects.workspaceId,
86
+ count: count()
87
+ }).from(projects).where(inArray(projects.workspaceId, params.workspaceIds)).groupBy(projects.workspaceId);
88
+ const countsByWorkspace = new Map(rows.map((row)=>[
89
+ row.workspaceId,
90
+ Number(row.count)
91
+ ]));
92
+ return params.workspaceIds.map((workspaceId)=>({
93
+ workspaceId,
94
+ count: countsByWorkspace.get(workspaceId) ?? 0
95
+ }));
96
+ }
83
97
 
84
98
  //# 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":"AAAA,SAAQA,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAEC,EAAE,EAAEC,KAAK,EAAEC,EAAE,EAAEC,EAAE,QAAiB,cAAc;AAE1E,SAAQC,yBAAyB,EAAEC,oBAAoB,QAAO,kBAAkB;AAChF,SAAQC,oBAAoB,QAAO,uBAAuB;AAC1D,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,QAAQ,EAAEC,SAAS,QAAO,uBAAuB;AAqBzD,SAASC,mBAAmBC,KAAa;IACvC,OAAOA,MAAMC,OAAO,CAAC,WAAW,CAACC,OAAS,CAAC,EAAE,EAAEA,MAAM;AACvD;AAOA,SAASC,YAAYC,MAA0B;IAC7C,IAAI,CAACA,OAAOC,MAAM,EAAE,OAAOC;IAC3B,OAAOd,GACLD,GAAGM,SAASU,SAAS,EAAEH,OAAOC,MAAM,CAACE,SAAS,GAC9CrB,IAAIG,GAAGQ,SAASU,SAAS,EAAEH,OAAOC,MAAM,CAACE,SAAS,GAAGhB,GAAGM,SAASW,EAAE,EAAEJ,OAAOC,MAAM,CAACG,EAAE;AAEzF;AAEA,OAAO,eAAeC,cAAcL,MAA2B;IAC7D,MAAMM,UAAU,MAAMd,KAAKe,WAAW,CAAC,OAAOC;QAC5C,MAAM,CAACC,WAAW,GAAG,MAAMD,GACxBE,MAAM,CAACjB,UACPkB,MAAM,CAAC;YACNC,aAAaZ,OAAOY,WAAW;YAC/BC,oBAAoBb,OAAOa,kBAAkB;YAC7CC,4BAA4Bd,OAAOc,0BAA0B;YAC7DC,MAAMf,OAAOe,IAAI;QACnB,GACCC,mBAAmB,CAAC;YACnBC,QAAQ;gBAACxB,SAASoB,kBAAkB;gBAAEpB,SAASqB,0BAA0B;aAAC;QAC5E,GACCI,SAAS;QAEZ,IAAI,CAACT,YAAY;YACf,MAAM,CAACU,SAAS,GAAG,MAAMX,GACtBY,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CACJxC,IACEG,GAAGQ,SAASoB,kBAAkB,EAAEb,OAAOa,kBAAkB,GACzD5B,GAAGQ,SAASqB,0BAA0B,EAAEd,OAAOc,0BAA0B,IAG5ES,KAAK,CAAC;YACT,IAAIJ,UAAU;gBACZ,MAAM,IAAI9B,0BACR8B,SAASf,EAAE,EACXJ,OAAOa,kBAAkB,EACzBb,OAAOc,0BAA0B;YAErC;YACA,MAAM,IAAIU,MAAM;QAClB;QAEA,OAAO9B,UAAUe;IACnB;IACAlB;IACA,OAAOe;AACT;AAEA,OAAO,eAAemB,eAAerB,EAAU;IAC7C,MAAMsB,OAAO,MAAMlC,KAAK4B,MAAM,GAAGC,IAAI,CAAC5B,UAAU6B,KAAK,CAACrC,GAAGQ,SAASW,EAAE,EAAEA,KAAKmB,KAAK,CAAC;IAEjF,MAAMI,MAAMD,IAAI,CAAC,EAAE;IACnB,IAAI,CAACC,KAAK,OAAOzB;IACjB,OAAOR,UAAUiC;AACnB;AAQA,OAAO,eAAeC,mBACpB5B,MAAgC;IAEhC,MAAM0B,OAAO,MAAMlC,KAChB4B,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CACJxC,IACEG,GAAGQ,SAASmB,WAAW,EAAEZ,OAAOY,WAAW,GAC3C3B,GAAGQ,SAASoB,kBAAkB,EAAEb,OAAOa,kBAAkB,GACzD5B,GAAGQ,SAASqB,0BAA0B,EAAEd,OAAOc,0BAA0B,IAG5ES,KAAK,CAAC;IAET,MAAMI,MAAMD,IAAI,CAAC,EAAE;IACnB,IAAI,CAACC,KAAK,OAAOzB;IACjB,OAAOR,UAAUiC;AACnB;AAEA,OAAO,eAAeE,2BAA2B7B,MAGhD;IACC,MAAMM,UAAU,MAAMmB,eAAezB,OAAO8B,SAAS;IACrD,IAAI,CAACxB,SAAS,MAAM,IAAIhB,qBAAqBU,OAAO8B,SAAS;IAC7D,IAAIxB,QAAQM,WAAW,KAAKZ,OAAOY,WAAW,EAAE,MAAM,IAAItB,qBAAqBU,OAAO8B,SAAS;IAC/F,OAAOxB;AACT;AAEA,OAAO,eAAeyB,aAAa/B,MAA0B;IAC3D,MAAMgC,aAAa;QAAC/C,GAAGQ,SAASmB,WAAW,EAAEZ,OAAOY,WAAW;KAAE;IACjE,MAAMqB,kBAAkBlC,YAAYC;IACpC,IAAIiC,iBAAiBD,WAAWE,IAAI,CAACD;IACrC,IAAIjC,OAAOmC,MAAM,EAAE;QACjBH,WAAWE,IAAI,CAAChD,MAAMO,SAASsB,IAAI,EAAE,CAAC,CAAC,EAAEpB,mBAAmBK,OAAOmC,MAAM,EAAE,CAAC,CAAC;IAC/E;IAEA,MAAMT,OAAO,MAAMlC,KAChB4B,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CAACxC,OAAOkD,aACbI,OAAO,CAACpD,KAAKS,SAASU,SAAS,GAAGnB,KAAKS,SAASW,EAAE,GAClDmB,KAAK,CAACvB,OAAOuB,KAAK,GAAG;IAExB,MAAMc,UAAUX,KAAKY,MAAM,GAAGtC,OAAOuB,KAAK;IAC1C,MAAMgB,WAAWF,UAAUX,KAAKc,KAAK,CAAC,GAAGxC,OAAOuB,KAAK,IAAIG;IACzD,MAAMe,OAAOF,SAASG,EAAE,CAAC,CAAC;IAE1B,OAAO;QACLjD,UAAU8C,SAASI,GAAG,CAACjD;QACvBkD,YAAYP,WAAWI,OAAO;YAACtC,WAAWsC,KAAKtC,SAAS;YAAEC,IAAIqC,KAAKrC,EAAE;QAAA,IAAI;IAC3E;AACF;AAEA,OAAO,eAAeyC;IACpB,MAAM,CAAClB,IAAI,GAAG,MAAMnC,KAAK4B,MAAM,CAAC;QAACxB,OAAOb;IAAO,GAAGsC,IAAI,CAAC5B;IACvD,OAAOkC,KAAK/B,SAAS;AACvB"}
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\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\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","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","getWorkspaceProjectCounts","workspaceIds","groupBy","countsByWorkspace","Map","Number","get"],"mappings":"AAAA,SAAQA,GAAG,EAAEC,KAAK,EAAEC,IAAI,EAAEC,EAAE,EAAEC,KAAK,EAAEC,OAAO,EAAEC,EAAE,EAAEC,EAAE,QAAiB,cAAc;AAEnF,SAAQC,yBAAyB,EAAEC,oBAAoB,QAAO,kBAAkB;AAChF,SAAQC,oBAAoB,QAAO,uBAAuB;AAC1D,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,QAAQ,EAAEC,SAAS,QAAO,uBAAuB;AAqBzD,SAASC,mBAAmBC,KAAa;IACvC,OAAOA,MAAMC,OAAO,CAAC,WAAW,CAACC,OAAS,CAAC,EAAE,EAAEA,MAAM;AACvD;AAOA,SAASC,YAAYC,MAA0B;IAC7C,IAAI,CAACA,OAAOC,MAAM,EAAE,OAAOC;IAC3B,OAAOd,GACLD,GAAGM,SAASU,SAAS,EAAEH,OAAOC,MAAM,CAACE,SAAS,GAC9CtB,IAAIG,GAAGS,SAASU,SAAS,EAAEH,OAAOC,MAAM,CAACE,SAAS,GAAGhB,GAAGM,SAASW,EAAE,EAAEJ,OAAOC,MAAM,CAACG,EAAE;AAEzF;AAEA,OAAO,eAAeC,cAAcL,MAA2B;IAC7D,MAAMM,UAAU,MAAMd,KAAKe,WAAW,CAAC,OAAOC;QAC5C,MAAM,CAACC,WAAW,GAAG,MAAMD,GACxBE,MAAM,CAACjB,UACPkB,MAAM,CAAC;YACNC,aAAaZ,OAAOY,WAAW;YAC/BC,oBAAoBb,OAAOa,kBAAkB;YAC7CC,4BAA4Bd,OAAOc,0BAA0B;YAC7DC,MAAMf,OAAOe,IAAI;QACnB,GACCC,mBAAmB,CAAC;YACnBC,QAAQ;gBAACxB,SAASoB,kBAAkB;gBAAEpB,SAASqB,0BAA0B;aAAC;QAC5E,GACCI,SAAS;QAEZ,IAAI,CAACT,YAAY;YACf,MAAM,CAACU,SAAS,GAAG,MAAMX,GACtBY,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CACJzC,IACEG,GAAGS,SAASoB,kBAAkB,EAAEb,OAAOa,kBAAkB,GACzD7B,GAAGS,SAASqB,0BAA0B,EAAEd,OAAOc,0BAA0B,IAG5ES,KAAK,CAAC;YACT,IAAIJ,UAAU;gBACZ,MAAM,IAAI9B,0BACR8B,SAASf,EAAE,EACXJ,OAAOa,kBAAkB,EACzBb,OAAOc,0BAA0B;YAErC;YACA,MAAM,IAAIU,MAAM;QAClB;QAEA,OAAO9B,UAAUe;IACnB;IACAlB;IACA,OAAOe;AACT;AAEA,OAAO,eAAemB,eAAerB,EAAU;IAC7C,MAAMsB,OAAO,MAAMlC,KAAK4B,MAAM,GAAGC,IAAI,CAAC5B,UAAU6B,KAAK,CAACtC,GAAGS,SAASW,EAAE,EAAEA,KAAKmB,KAAK,CAAC;IAEjF,MAAMI,MAAMD,IAAI,CAAC,EAAE;IACnB,IAAI,CAACC,KAAK,OAAOzB;IACjB,OAAOR,UAAUiC;AACnB;AAQA,OAAO,eAAeC,mBACpB5B,MAAgC;IAEhC,MAAM0B,OAAO,MAAMlC,KAChB4B,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CACJzC,IACEG,GAAGS,SAASmB,WAAW,EAAEZ,OAAOY,WAAW,GAC3C5B,GAAGS,SAASoB,kBAAkB,EAAEb,OAAOa,kBAAkB,GACzD7B,GAAGS,SAASqB,0BAA0B,EAAEd,OAAOc,0BAA0B,IAG5ES,KAAK,CAAC;IAET,MAAMI,MAAMD,IAAI,CAAC,EAAE;IACnB,IAAI,CAACC,KAAK,OAAOzB;IACjB,OAAOR,UAAUiC;AACnB;AAEA,OAAO,eAAeE,2BAA2B7B,MAGhD;IACC,MAAMM,UAAU,MAAMmB,eAAezB,OAAO8B,SAAS;IACrD,IAAI,CAACxB,SAAS,MAAM,IAAIhB,qBAAqBU,OAAO8B,SAAS;IAC7D,IAAIxB,QAAQM,WAAW,KAAKZ,OAAOY,WAAW,EAAE,MAAM,IAAItB,qBAAqBU,OAAO8B,SAAS;IAC/F,OAAOxB;AACT;AAEA,OAAO,eAAeyB,aAAa/B,MAA0B;IAC3D,MAAMgC,aAAa;QAAChD,GAAGS,SAASmB,WAAW,EAAEZ,OAAOY,WAAW;KAAE;IACjE,MAAMqB,kBAAkBlC,YAAYC;IACpC,IAAIiC,iBAAiBD,WAAWE,IAAI,CAACD;IACrC,IAAIjC,OAAOmC,MAAM,EAAE;QACjBH,WAAWE,IAAI,CAACjD,MAAMQ,SAASsB,IAAI,EAAE,CAAC,CAAC,EAAEpB,mBAAmBK,OAAOmC,MAAM,EAAE,CAAC,CAAC;IAC/E;IAEA,MAAMT,OAAO,MAAMlC,KAChB4B,MAAM,GACNC,IAAI,CAAC5B,UACL6B,KAAK,CAACzC,OAAOmD,aACbI,OAAO,CAACrD,KAAKU,SAASU,SAAS,GAAGpB,KAAKU,SAASW,EAAE,GAClDmB,KAAK,CAACvB,OAAOuB,KAAK,GAAG;IAExB,MAAMc,UAAUX,KAAKY,MAAM,GAAGtC,OAAOuB,KAAK;IAC1C,MAAMgB,WAAWF,UAAUX,KAAKc,KAAK,CAAC,GAAGxC,OAAOuB,KAAK,IAAIG;IACzD,MAAMe,OAAOF,SAASG,EAAE,CAAC,CAAC;IAE1B,OAAO;QACLjD,UAAU8C,SAASI,GAAG,CAACjD;QACvBkD,YAAYP,WAAWI,OAAO;YAACtC,WAAWsC,KAAKtC,SAAS;YAAEC,IAAIqC,KAAKrC,EAAE;QAAA,IAAI;IAC3E;AACF;AAEA,OAAO,eAAeyC;IACpB,MAAM,CAAClB,IAAI,GAAG,MAAMnC,KAAK4B,MAAM,CAAC;QAACxB,OAAOd;IAAO,GAAGuC,IAAI,CAAC5B;IACvD,OAAOkC,KAAK/B,SAAS;AACvB;AAEA,OAAO,eAAekD,0BAA0B9C,MAE/C;IACC,MAAM0B,OAAO,MAAMlC,KAChB4B,MAAM,CAAC;QAACR,aAAanB,SAASmB,WAAW;QAAE9B,OAAOA;IAAO,GACzDuC,IAAI,CAAC5B,UACL6B,KAAK,CAACpC,QAAQO,SAASmB,WAAW,EAAEZ,OAAO+C,YAAY,GACvDC,OAAO,CAACvD,SAASmB,WAAW;IAE/B,MAAMqC,oBAAoB,IAAIC,IAAIxB,KAAKiB,GAAG,CAAC,CAAChB,MAAQ;YAACA,IAAIf,WAAW;YAAEuC,OAAOxB,IAAI7C,KAAK;SAAE;IACxF,OAAOkB,OAAO+C,YAAY,CAACJ,GAAG,CAAC,CAAC/B,cAAiB,CAAA;YAC/CA;YACA9B,OAAOmE,kBAAkBG,GAAG,CAACxC,gBAAgB;QAC/C,CAAA;AACF"}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { type ShipfoxModule } from '@shipfox/node-module';
3
3
  export type { Project } from '#core/index.js';
4
4
  export { createProjectFromSource, ProjectAccessDeniedError, ProjectAlreadyExistsError, ProjectNotFoundError, } from '#core/index.js';
5
5
  export type { GetProjectBySourceParams } from '#db/index.js';
6
- export { createProject, db, getProjectById, getProjectBySource, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace, } from '#db/index.js';
6
+ export { createProject, db, getProjectById, getProjectBySource, getWorkspaceProjectCounts, listProjects, migrationsPath, projectsOutbox, requireProjectForWorkspace, } from '#db/index.js';
7
7
  export { createProjectRoutes, requireProjectAccess } from '#presentation/index.js';
8
8
  export interface CreateProjectsModuleOptions {
9
9
  integrations: IntegrationsModuleClient;
@@ -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":"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,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;CACxC;AAED,wBAAgB,oBAAoB,CAAC,EAAC,YAAY,EAAC,EAAE,2BAA2B,GAAG,aAAa,CAyB/F"}
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ 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
20
  export function createProjectsModule({ integrations }) {
21
21
  return {
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 {\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}\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","getWorkspaceProjectCounts","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,yBAAyB,EACzBC,YAAY,EACZrB,cAAc,EACdC,cAAc,EACdqB,0BAA0B,QACrB,eAAe;AACtB,SAAQlB,mBAAmB,EAAEmB,oBAAoB,QAAO,yBAAyB;AAMjF,OAAO,SAASC,qBAAqB,EAACC,YAAY,EAA8B;IAC9E,OAAO;QACLC,MAAM;QACNC,UAAU;YAAC5B;YAAIC;YAAgB4B,mBAAmB;QAAU;QAC5DC,QAAQzB,oBAAoBqB;QAC5BK,WAAW;YAAC3B;SAAkB;QAC9B4B,SAAS7B;QACT8B,YAAY;YAAC;gBAACN,MAAM;gBAAYO,OAAOhC;gBAAgBF;gBAAImC,cAAcrC;YAAoB;SAAE;QAC/FsC,0BAA0B;YAAC9B;SAAwC;QACnE+B,aAAa;YAACxB,WAAWhB,kCAAkCU;SAAsB;QACjF+B,SAAS;YACP;gBACEC,WAAW9B;gBACX+B,eAAe5B;gBACf6B,YAAYjC;gBACZkC,WAAW;oBACT;wBACEf,MAAM;wBACNgB,IAAI;wBACJC,cAAc;oBAChB;iBACD;YACH;SACD;IACH;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"}