@shipfox/api-projects 11.0.0 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +53 -0
- package/README.md +83 -0
- package/dist/core/entities/project.d.ts +4 -0
- package/dist/core/entities/project.d.ts.map +1 -1
- package/dist/core/entities/project.js.map +1 -1
- package/dist/core/errors.d.ts +4 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +6 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/projects.d.ts +8 -0
- package/dist/core/projects.d.ts.map +1 -1
- package/dist/core/projects.js +62 -19
- package/dist/core/projects.js.map +1 -1
- package/dist/db/db.d.ts +144 -6
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/projects.d.ts +53 -0
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/db/projects.js +116 -18
- package/dist/db/projects.js.map +1 -1
- package/dist/db/schema/integration-event-dedup.d.ts +1 -1
- package/dist/db/schema/outbox.d.ts +1 -1
- package/dist/db/schema/projects.d.ts +69 -1
- package/dist/db/schema/projects.d.ts.map +1 -1
- package/dist/db/schema/projects.js +12 -1
- package/dist/db/schema/projects.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/presentation/dto/project.d.ts +2 -1
- package/dist/presentation/dto/project.d.ts.map +1 -1
- package/dist/presentation/dto/project.js +1 -0
- package/dist/presentation/dto/project.js.map +1 -1
- package/dist/presentation/e2eRoutes/create-project.d.ts.map +1 -1
- package/dist/presentation/e2eRoutes/create-project.js +34 -8
- package/dist/presentation/e2eRoutes/create-project.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +4 -1
- package/dist/presentation/inter-module.d.ts.map +1 -1
- package/dist/presentation/inter-module.js +63 -3
- package/dist/presentation/inter-module.js.map +1 -1
- package/dist/presentation/routes/create-project.d.ts.map +1 -1
- package/dist/presentation/routes/create-project.js +8 -2
- package/dist/presentation/routes/create-project.js.map +1 -1
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +3 -1
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/update-project.d.ts +2 -0
- package/dist/presentation/routes/update-project.d.ts.map +1 -0
- package/dist/presentation/routes/update-project.js +53 -0
- package/dist/presentation/routes/update-project.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +1 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -1
- package/dist/presentation/subscribers/index.js +1 -0
- package/dist/presentation/subscribers/index.js.map +1 -1
- package/dist/presentation/subscribers/on-source-repository-updated.d.ts +4 -0
- package/dist/presentation/subscribers/on-source-repository-updated.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-source-repository-updated.js +39 -0
- package/dist/presentation/subscribers/on-source-repository-updated.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_initial.sql +6 -0
- package/drizzle/meta/0000_snapshot.json +78 -0
- package/package.json +13 -12
- package/src/core/entities/project.ts +4 -0
- package/src/core/errors.ts +7 -0
- package/src/core/index.ts +3 -2
- package/src/core/projects.test.ts +10 -0
- package/src/core/projects.ts +90 -35
- package/src/db/db.ts +3 -0
- package/src/db/index.ts +6 -0
- package/src/db/projects.test.ts +44 -1
- package/src/db/projects.ts +242 -33
- package/src/db/schema/projects.ts +16 -0
- package/src/index.ts +9 -3
- package/src/presentation/dto/project.ts +1 -0
- package/src/presentation/e2eRoutes/create-project.test.ts +23 -0
- package/src/presentation/e2eRoutes/create-project.ts +39 -10
- package/src/presentation/inter-module.test.ts +349 -0
- package/src/presentation/inter-module.ts +90 -4
- package/src/presentation/routes/create-project.ts +10 -2
- package/src/presentation/routes/index.ts +7 -1
- package/src/presentation/routes/projects.test.ts +300 -4
- package/src/presentation/routes/update-project.ts +46 -0
- package/src/presentation/subscribers/index.ts +1 -0
- package/src/presentation/subscribers/on-source-commit-pushed.test.ts +2 -0
- package/src/presentation/subscribers/on-source-repository-updated.test.ts +108 -0
- package/src/presentation/subscribers/on-source-repository-updated.ts +50 -0
- package/test/factories/project.ts +6 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/dist/db/db.d.ts
CHANGED
|
@@ -72,6 +72,57 @@ export declare const schema: {
|
|
|
72
72
|
identity: undefined;
|
|
73
73
|
generated: undefined;
|
|
74
74
|
}, {}, {}>;
|
|
75
|
+
sourceRepositoryOwner: import("drizzle-orm/pg-core").PgColumn<{
|
|
76
|
+
name: "source_repository_owner";
|
|
77
|
+
tableName: "projects";
|
|
78
|
+
dataType: "string";
|
|
79
|
+
columnType: "PgText";
|
|
80
|
+
data: string;
|
|
81
|
+
driverParam: string;
|
|
82
|
+
notNull: false;
|
|
83
|
+
hasDefault: false;
|
|
84
|
+
isPrimaryKey: false;
|
|
85
|
+
isAutoincrement: false;
|
|
86
|
+
hasRuntimeDefault: false;
|
|
87
|
+
enumValues: [string, ...string[]];
|
|
88
|
+
baseColumn: never;
|
|
89
|
+
identity: undefined;
|
|
90
|
+
generated: undefined;
|
|
91
|
+
}, {}, {}>;
|
|
92
|
+
sourceRepositoryName: import("drizzle-orm/pg-core").PgColumn<{
|
|
93
|
+
name: "source_repository_name";
|
|
94
|
+
tableName: "projects";
|
|
95
|
+
dataType: "string";
|
|
96
|
+
columnType: "PgText";
|
|
97
|
+
data: string;
|
|
98
|
+
driverParam: string;
|
|
99
|
+
notNull: false;
|
|
100
|
+
hasDefault: false;
|
|
101
|
+
isPrimaryKey: false;
|
|
102
|
+
isAutoincrement: false;
|
|
103
|
+
hasRuntimeDefault: false;
|
|
104
|
+
enumValues: [string, ...string[]];
|
|
105
|
+
baseColumn: never;
|
|
106
|
+
identity: undefined;
|
|
107
|
+
generated: undefined;
|
|
108
|
+
}, {}, {}>;
|
|
109
|
+
sourceDefaultBranch: import("drizzle-orm/pg-core").PgColumn<{
|
|
110
|
+
name: "source_default_branch";
|
|
111
|
+
tableName: "projects";
|
|
112
|
+
dataType: "string";
|
|
113
|
+
columnType: "PgText";
|
|
114
|
+
data: string;
|
|
115
|
+
driverParam: string;
|
|
116
|
+
notNull: false;
|
|
117
|
+
hasDefault: false;
|
|
118
|
+
isPrimaryKey: false;
|
|
119
|
+
isAutoincrement: false;
|
|
120
|
+
hasRuntimeDefault: false;
|
|
121
|
+
enumValues: [string, ...string[]];
|
|
122
|
+
baseColumn: never;
|
|
123
|
+
identity: undefined;
|
|
124
|
+
generated: undefined;
|
|
125
|
+
}, {}, {}>;
|
|
75
126
|
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
76
127
|
name: "name";
|
|
77
128
|
tableName: "projects";
|
|
@@ -89,6 +140,23 @@ export declare const schema: {
|
|
|
89
140
|
identity: undefined;
|
|
90
141
|
generated: undefined;
|
|
91
142
|
}, {}, {}>;
|
|
143
|
+
slug: import("drizzle-orm/pg-core").PgColumn<{
|
|
144
|
+
name: "slug";
|
|
145
|
+
tableName: "projects";
|
|
146
|
+
dataType: "string";
|
|
147
|
+
columnType: "PgText";
|
|
148
|
+
data: string;
|
|
149
|
+
driverParam: string;
|
|
150
|
+
notNull: true;
|
|
151
|
+
hasDefault: false;
|
|
152
|
+
isPrimaryKey: false;
|
|
153
|
+
isAutoincrement: false;
|
|
154
|
+
hasRuntimeDefault: false;
|
|
155
|
+
enumValues: [string, ...string[]];
|
|
156
|
+
baseColumn: never;
|
|
157
|
+
identity: undefined;
|
|
158
|
+
generated: undefined;
|
|
159
|
+
}, {}, {}>;
|
|
92
160
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
93
161
|
name: "created_at";
|
|
94
162
|
tableName: "projects";
|
|
@@ -124,7 +192,7 @@ export declare const schema: {
|
|
|
124
192
|
generated: undefined;
|
|
125
193
|
}, {}, {}>;
|
|
126
194
|
};
|
|
127
|
-
dialect:
|
|
195
|
+
dialect: 'pg';
|
|
128
196
|
}>;
|
|
129
197
|
projectsOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
130
198
|
name: "outbox";
|
|
@@ -318,7 +386,7 @@ export declare const schema: {
|
|
|
318
386
|
generated: undefined;
|
|
319
387
|
}, {}, {}>;
|
|
320
388
|
};
|
|
321
|
-
dialect:
|
|
389
|
+
dialect: 'pg';
|
|
322
390
|
}>;
|
|
323
391
|
projectsIntegrationEventDedup: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
324
392
|
name: "integration_event_dedup";
|
|
@@ -376,7 +444,7 @@ export declare const schema: {
|
|
|
376
444
|
generated: undefined;
|
|
377
445
|
}, {}, {}>;
|
|
378
446
|
};
|
|
379
|
-
dialect:
|
|
447
|
+
dialect: 'pg';
|
|
380
448
|
}>;
|
|
381
449
|
};
|
|
382
450
|
export declare function db(): NodePgDatabase<{
|
|
@@ -452,6 +520,57 @@ export declare function db(): NodePgDatabase<{
|
|
|
452
520
|
identity: undefined;
|
|
453
521
|
generated: undefined;
|
|
454
522
|
}, {}, {}>;
|
|
523
|
+
sourceRepositoryOwner: import("drizzle-orm/pg-core").PgColumn<{
|
|
524
|
+
name: "source_repository_owner";
|
|
525
|
+
tableName: "projects";
|
|
526
|
+
dataType: "string";
|
|
527
|
+
columnType: "PgText";
|
|
528
|
+
data: string;
|
|
529
|
+
driverParam: string;
|
|
530
|
+
notNull: false;
|
|
531
|
+
hasDefault: false;
|
|
532
|
+
isPrimaryKey: false;
|
|
533
|
+
isAutoincrement: false;
|
|
534
|
+
hasRuntimeDefault: false;
|
|
535
|
+
enumValues: [string, ...string[]];
|
|
536
|
+
baseColumn: never;
|
|
537
|
+
identity: undefined;
|
|
538
|
+
generated: undefined;
|
|
539
|
+
}, {}, {}>;
|
|
540
|
+
sourceRepositoryName: import("drizzle-orm/pg-core").PgColumn<{
|
|
541
|
+
name: "source_repository_name";
|
|
542
|
+
tableName: "projects";
|
|
543
|
+
dataType: "string";
|
|
544
|
+
columnType: "PgText";
|
|
545
|
+
data: string;
|
|
546
|
+
driverParam: string;
|
|
547
|
+
notNull: false;
|
|
548
|
+
hasDefault: false;
|
|
549
|
+
isPrimaryKey: false;
|
|
550
|
+
isAutoincrement: false;
|
|
551
|
+
hasRuntimeDefault: false;
|
|
552
|
+
enumValues: [string, ...string[]];
|
|
553
|
+
baseColumn: never;
|
|
554
|
+
identity: undefined;
|
|
555
|
+
generated: undefined;
|
|
556
|
+
}, {}, {}>;
|
|
557
|
+
sourceDefaultBranch: import("drizzle-orm/pg-core").PgColumn<{
|
|
558
|
+
name: "source_default_branch";
|
|
559
|
+
tableName: "projects";
|
|
560
|
+
dataType: "string";
|
|
561
|
+
columnType: "PgText";
|
|
562
|
+
data: string;
|
|
563
|
+
driverParam: string;
|
|
564
|
+
notNull: false;
|
|
565
|
+
hasDefault: false;
|
|
566
|
+
isPrimaryKey: false;
|
|
567
|
+
isAutoincrement: false;
|
|
568
|
+
hasRuntimeDefault: false;
|
|
569
|
+
enumValues: [string, ...string[]];
|
|
570
|
+
baseColumn: never;
|
|
571
|
+
identity: undefined;
|
|
572
|
+
generated: undefined;
|
|
573
|
+
}, {}, {}>;
|
|
455
574
|
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
456
575
|
name: "name";
|
|
457
576
|
tableName: "projects";
|
|
@@ -469,6 +588,23 @@ export declare function db(): NodePgDatabase<{
|
|
|
469
588
|
identity: undefined;
|
|
470
589
|
generated: undefined;
|
|
471
590
|
}, {}, {}>;
|
|
591
|
+
slug: import("drizzle-orm/pg-core").PgColumn<{
|
|
592
|
+
name: "slug";
|
|
593
|
+
tableName: "projects";
|
|
594
|
+
dataType: "string";
|
|
595
|
+
columnType: "PgText";
|
|
596
|
+
data: string;
|
|
597
|
+
driverParam: string;
|
|
598
|
+
notNull: true;
|
|
599
|
+
hasDefault: false;
|
|
600
|
+
isPrimaryKey: false;
|
|
601
|
+
isAutoincrement: false;
|
|
602
|
+
hasRuntimeDefault: false;
|
|
603
|
+
enumValues: [string, ...string[]];
|
|
604
|
+
baseColumn: never;
|
|
605
|
+
identity: undefined;
|
|
606
|
+
generated: undefined;
|
|
607
|
+
}, {}, {}>;
|
|
472
608
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
473
609
|
name: "created_at";
|
|
474
610
|
tableName: "projects";
|
|
@@ -504,7 +640,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
504
640
|
generated: undefined;
|
|
505
641
|
}, {}, {}>;
|
|
506
642
|
};
|
|
507
|
-
dialect:
|
|
643
|
+
dialect: 'pg';
|
|
508
644
|
}>;
|
|
509
645
|
projectsOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
510
646
|
name: "outbox";
|
|
@@ -698,7 +834,7 @@ export declare function db(): NodePgDatabase<{
|
|
|
698
834
|
generated: undefined;
|
|
699
835
|
}, {}, {}>;
|
|
700
836
|
};
|
|
701
|
-
dialect:
|
|
837
|
+
dialect: 'pg';
|
|
702
838
|
}>;
|
|
703
839
|
projectsIntegrationEventDedup: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
704
840
|
name: "integration_event_dedup";
|
|
@@ -756,8 +892,10 @@ export declare function db(): NodePgDatabase<{
|
|
|
756
892
|
generated: undefined;
|
|
757
893
|
}, {}, {}>;
|
|
758
894
|
};
|
|
759
|
-
dialect:
|
|
895
|
+
dialect: 'pg';
|
|
760
896
|
}>;
|
|
761
897
|
}>;
|
|
898
|
+
export type Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];
|
|
899
|
+
export type Executor = ReturnType<typeof db> | Tx;
|
|
762
900
|
export declare function closeDb(): void;
|
|
763
901
|
//# sourceMappingURL=db.d.ts.map
|
package/dist/db/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAMnE,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAMnE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIlB,CAAC;AAIF,wBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGjB;AAED,MAAM,MAAM,EAAE,GAAG,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpF,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC;AAElD,wBAAgB,OAAO,IAAI,IAAI,CAE9B"}
|
package/dist/db/db.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/db.ts"],"sourcesContent":["import {drizzle, type NodePgDatabase} from '@shipfox/node-drizzle';\nimport {pgClient} from '@shipfox/node-postgres';\nimport {projectsIntegrationEventDedup} from './schema/integration-event-dedup.js';\nimport {projectsOutbox} from './schema/outbox.js';\nimport {projects} from './schema/projects.js';\n\nexport const schema = {\n projects,\n projectsOutbox,\n projectsIntegrationEventDedup,\n};\n\nlet _db: NodePgDatabase<typeof schema> | undefined;\n\nexport function db() {\n if (!_db) _db = drizzle(pgClient(), {schema});\n return _db;\n}\n\nexport function closeDb(): void {\n _db = undefined;\n}\n"],"names":["drizzle","pgClient","projectsIntegrationEventDedup","projectsOutbox","projects","schema","_db","db","closeDb","undefined"],"mappings":"AAAA,SAAQA,OAAO,QAA4B,wBAAwB;AACnE,SAAQC,QAAQ,QAAO,yBAAyB;AAChD,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAClD,SAAQC,QAAQ,QAAO,uBAAuB;AAE9C,OAAO,MAAMC,SAAS;IACpBD;IACAD;IACAD;AACF,EAAE;AAEF,IAAII;AAEJ,OAAO,SAASC;IACd,IAAI,CAACD,KAAKA,MAAMN,QAAQC,YAAY;QAACI;IAAM;IAC3C,OAAOC;AACT;
|
|
1
|
+
{"version":3,"sources":["../../src/db/db.ts"],"sourcesContent":["import {drizzle, type NodePgDatabase} from '@shipfox/node-drizzle';\nimport {pgClient} from '@shipfox/node-postgres';\nimport {projectsIntegrationEventDedup} from './schema/integration-event-dedup.js';\nimport {projectsOutbox} from './schema/outbox.js';\nimport {projects} from './schema/projects.js';\n\nexport const schema = {\n projects,\n projectsOutbox,\n projectsIntegrationEventDedup,\n};\n\nlet _db: NodePgDatabase<typeof schema> | undefined;\n\nexport function db() {\n if (!_db) _db = drizzle(pgClient(), {schema});\n return _db;\n}\n\nexport type Tx = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];\nexport type Executor = ReturnType<typeof db> | Tx;\n\nexport function closeDb(): void {\n _db = undefined;\n}\n"],"names":["drizzle","pgClient","projectsIntegrationEventDedup","projectsOutbox","projects","schema","_db","db","closeDb","undefined"],"mappings":"AAAA,SAAQA,OAAO,QAA4B,wBAAwB;AACnE,SAAQC,QAAQ,QAAO,yBAAyB;AAChD,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAClD,SAAQC,QAAQ,QAAO,uBAAuB;AAE9C,OAAO,MAAMC,SAAS;IACpBD;IACAD;IACAD;AACF,EAAE;AAEF,IAAII;AAEJ,OAAO,SAASC;IACd,IAAI,CAACD,KAAKA,MAAMN,QAAQC,YAAY;QAACI;IAAM;IAC3C,OAAOC;AACT;AAKA,OAAO,SAASE;IACdF,MAAMG;AACR"}
|
package/dist/db/index.d.ts
CHANGED
|
@@ -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 { AdminProjectSummary, CreateProjectParams, GetProjectBySourceParams, ListAdminProjectsParams, ListAdminProjectsResult, ListProjectsParams, ListProjectsResult, } from './projects.js';
|
|
4
|
-
export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace, } from './projects.js';
|
|
3
|
+
export type { AdminProjectSummary, CreateProjectParams, GetProjectBySourceParams, ListAdminProjectsParams, ListAdminProjectsResult, ListProjectsParams, ListProjectsResult, ResolveCheckoutTargetParams, ResolvedCheckoutTarget, UpdateProjectParams, UpdateProjectResult, } from './projects.js';
|
|
4
|
+
export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace, resolveCheckoutTarget, updateProject, } 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;
|
package/dist/db/index.d.ts.map
CHANGED
|
@@ -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,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,
|
|
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,EAClB,2BAA2B,EAC3B,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,YAAY,EACZ,0BAA0B,EAC1B,qBAAqB,EACrB,aAAa,GACd,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, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace } from './projects.js';
|
|
5
|
+
export { createProject, getProjectById, getProjectBySource, getProjectCount, getWorkspaceProjectCounts, listAdminProjects, listProjects, requireProjectForWorkspace, resolveCheckoutTarget, updateProject } 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');
|
package/dist/db/index.js.map
CHANGED
|
@@ -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 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;
|
|
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 ResolveCheckoutTargetParams,\n ResolvedCheckoutTarget,\n UpdateProjectParams,\n UpdateProjectResult,\n} from './projects.js';\nexport {\n createProject,\n getProjectById,\n getProjectBySource,\n getProjectCount,\n getWorkspaceProjectCounts,\n listAdminProjects,\n listProjects,\n requireProjectForWorkspace,\n resolveCheckoutTarget,\n updateProject,\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","resolveCheckoutTarget","updateProject","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;AActC,SACEC,aAAa,EACbC,cAAc,EACdC,kBAAkB,EAClBC,eAAe,EACfC,yBAAyB,EACzBC,iBAAiB,EACjBC,YAAY,EACZC,0BAA0B,EAC1BC,qBAAqB,EACrBC,aAAa,QACR,gBAAgB;AACvB,SAAQC,6BAA6B,QAAO,sCAAsC;AAClF,SAAQC,cAAc,QAAO,qBAAqB;AAElD,OAAO,MAAMC,iBAAiBnB,QAAQD,QAAQE,cAAc,YAAYmB,GAAG,IAAI,iBAAiB"}
|
package/dist/db/projects.d.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import type { Project } from '#core/entities/project.js';
|
|
2
|
+
import { db, type Executor } from './db.js';
|
|
3
|
+
type ProjectDatabase = ReturnType<typeof db>;
|
|
4
|
+
type ProjectTransaction = Parameters<Parameters<ProjectDatabase['transaction']>[0]>[0];
|
|
2
5
|
export interface CreateProjectParams {
|
|
3
6
|
workspaceId: string;
|
|
4
7
|
sourceConnectionId: string;
|
|
5
8
|
sourceExternalRepositoryId: string;
|
|
6
9
|
name: string;
|
|
10
|
+
slug: string;
|
|
11
|
+
}
|
|
12
|
+
export interface UpdateProjectParams {
|
|
13
|
+
projectId: string;
|
|
14
|
+
name?: string | undefined;
|
|
15
|
+
slug?: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface UpdateProjectResult {
|
|
18
|
+
project: Project;
|
|
19
|
+
changed: boolean;
|
|
7
20
|
}
|
|
8
21
|
export interface ProjectCursor {
|
|
9
22
|
createdAt: Date;
|
|
@@ -31,6 +44,9 @@ export interface ListAdminProjectsResult {
|
|
|
31
44
|
nextCursor: ProjectCursor | null;
|
|
32
45
|
}
|
|
33
46
|
export declare function createProject(params: CreateProjectParams): Promise<Project>;
|
|
47
|
+
export declare function updateProject(params: UpdateProjectParams, options?: {
|
|
48
|
+
tx?: ProjectDatabase | ProjectTransaction | undefined;
|
|
49
|
+
}): Promise<UpdateProjectResult | undefined>;
|
|
34
50
|
export declare function getProjectById(id: string): Promise<Project | undefined>;
|
|
35
51
|
export interface GetProjectBySourceParams {
|
|
36
52
|
workspaceId: string;
|
|
@@ -38,10 +54,46 @@ export interface GetProjectBySourceParams {
|
|
|
38
54
|
sourceExternalRepositoryId: string;
|
|
39
55
|
}
|
|
40
56
|
export declare function getProjectBySource(params: GetProjectBySourceParams): Promise<Project | undefined>;
|
|
57
|
+
export interface UpdateProjectSourceRepositoryParams extends GetProjectBySourceParams {
|
|
58
|
+
tx?: Executor;
|
|
59
|
+
sourceRepositoryOwner: string;
|
|
60
|
+
sourceRepositoryName: string;
|
|
61
|
+
sourceDefaultBranch: string;
|
|
62
|
+
}
|
|
63
|
+
export declare function updateProjectSourceRepository(params: UpdateProjectSourceRepositoryParams): Promise<Project | undefined>;
|
|
41
64
|
export declare function requireProjectForWorkspace(params: {
|
|
42
65
|
projectId: string;
|
|
43
66
|
workspaceId: string;
|
|
44
67
|
}): Promise<Project>;
|
|
68
|
+
export interface ResolveCheckoutTargetParams {
|
|
69
|
+
workspaceId: string;
|
|
70
|
+
defaults: {
|
|
71
|
+
connectionId: string;
|
|
72
|
+
owner: string;
|
|
73
|
+
};
|
|
74
|
+
target: {
|
|
75
|
+
project: string;
|
|
76
|
+
} | {
|
|
77
|
+
connection?: string | undefined;
|
|
78
|
+
repository: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export interface ResolvedCheckoutTarget {
|
|
82
|
+
projectId: string;
|
|
83
|
+
connectionId: string;
|
|
84
|
+
externalRepositoryId: string;
|
|
85
|
+
}
|
|
86
|
+
export interface UpdateProjectSourceMetadataParams {
|
|
87
|
+
workspaceId: string;
|
|
88
|
+
projectId: string;
|
|
89
|
+
sourceConnectionId: string;
|
|
90
|
+
sourceExternalRepositoryId: string;
|
|
91
|
+
sourceRepositoryOwner: string;
|
|
92
|
+
sourceRepositoryName: string;
|
|
93
|
+
sourceDefaultBranch: string;
|
|
94
|
+
}
|
|
95
|
+
export declare function updateProjectSourceMetadata(params: UpdateProjectSourceMetadataParams): Promise<void>;
|
|
96
|
+
export declare function resolveCheckoutTarget(params: ResolveCheckoutTargetParams): Promise<ResolvedCheckoutTarget | undefined>;
|
|
45
97
|
export declare function listProjects(params: ListProjectsParams): Promise<ListProjectsResult>;
|
|
46
98
|
export declare function listAdminProjects(params: ListAdminProjectsParams): Promise<ListAdminProjectsResult>;
|
|
47
99
|
export declare function getProjectCount(): Promise<number>;
|
|
@@ -51,4 +103,5 @@ export declare function getWorkspaceProjectCounts(params: {
|
|
|
51
103
|
workspaceId: string;
|
|
52
104
|
count: number;
|
|
53
105
|
}>>;
|
|
106
|
+
export {};
|
|
54
107
|
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"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"}
|
package/dist/db/projects.js
CHANGED
|
@@ -1,33 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isUniqueViolation } from '@shipfox/node-drizzle';
|
|
2
|
+
import { and, count, desc, eq, ilike, inArray, lt, or, sql } from 'drizzle-orm';
|
|
3
|
+
import { ProjectAlreadyExistsError, ProjectNotFoundError, ProjectSlugConflictError } from '#core/errors.js';
|
|
3
4
|
import { recordProjectCreated } from '#metrics/instance.js';
|
|
4
5
|
import { db } from './db.js';
|
|
5
6
|
import { projects, toProject } from './schema/projects.js';
|
|
6
7
|
function escapeIlikePattern(value) {
|
|
7
8
|
return value.replace(/[\\%_]/g, (char)=>`\\${char}`);
|
|
8
9
|
}
|
|
10
|
+
const PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT = 'projects_workspace_slug_unique';
|
|
9
11
|
function cursorWhere(cursor) {
|
|
10
12
|
if (!cursor) return undefined;
|
|
11
13
|
return or(lt(projects.createdAt, cursor.createdAt), and(eq(projects.createdAt, cursor.createdAt), lt(projects.id, cursor.id)));
|
|
12
14
|
}
|
|
13
15
|
export async function createProject(params) {
|
|
14
16
|
const project = await db().transaction(async (tx)=>{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
let projectRow;
|
|
18
|
+
for(let attempt = 0; attempt < 2 && !projectRow; attempt += 1){
|
|
19
|
+
try {
|
|
20
|
+
[projectRow] = await tx.insert(projects).values({
|
|
21
|
+
workspaceId: params.workspaceId,
|
|
22
|
+
sourceConnectionId: params.sourceConnectionId,
|
|
23
|
+
sourceExternalRepositoryId: params.sourceExternalRepositoryId,
|
|
24
|
+
name: params.name,
|
|
25
|
+
slug: params.slug
|
|
26
|
+
}).onConflictDoNothing({
|
|
27
|
+
target: [
|
|
28
|
+
projects.sourceConnectionId,
|
|
29
|
+
projects.sourceExternalRepositoryId
|
|
30
|
+
]
|
|
31
|
+
}).returning();
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {
|
|
34
|
+
throw new ProjectSlugConflictError(params.slug);
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
if (!projectRow) {
|
|
39
|
+
const [conflict] = await tx.select().from(projects).where(and(eq(projects.sourceConnectionId, params.sourceConnectionId), eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId))).limit(1);
|
|
40
|
+
if (conflict) {
|
|
41
|
+
throw new ProjectAlreadyExistsError(conflict.id, params.sourceConnectionId, params.sourceExternalRepositoryId);
|
|
42
|
+
}
|
|
30
43
|
}
|
|
44
|
+
}
|
|
45
|
+
if (!projectRow) {
|
|
31
46
|
throw new Error('Insert returned no rows');
|
|
32
47
|
}
|
|
33
48
|
return toProject(projectRow);
|
|
@@ -35,6 +50,39 @@ export async function createProject(params) {
|
|
|
35
50
|
recordProjectCreated();
|
|
36
51
|
return project;
|
|
37
52
|
}
|
|
53
|
+
export async function updateProject(params, options = {}) {
|
|
54
|
+
const executor = options.tx ?? db();
|
|
55
|
+
const [existingRow] = await executor.select().from(projects).where(eq(projects.id, params.projectId)).limit(1);
|
|
56
|
+
if (!existingRow) return undefined;
|
|
57
|
+
const nextName = params.name ?? existingRow.name;
|
|
58
|
+
const nextSlug = params.slug ?? existingRow.slug;
|
|
59
|
+
if (nextName === existingRow.name && nextSlug === existingRow.slug) {
|
|
60
|
+
return {
|
|
61
|
+
project: toProject(existingRow),
|
|
62
|
+
changed: false
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const [row] = await executor.update(projects).set({
|
|
67
|
+
...params.name !== undefined ? {
|
|
68
|
+
name: params.name
|
|
69
|
+
} : {},
|
|
70
|
+
...params.slug !== undefined ? {
|
|
71
|
+
slug: params.slug
|
|
72
|
+
} : {},
|
|
73
|
+
updatedAt: new Date()
|
|
74
|
+
}).where(eq(projects.id, params.projectId)).returning();
|
|
75
|
+
return row ? {
|
|
76
|
+
project: toProject(row),
|
|
77
|
+
changed: true
|
|
78
|
+
} : undefined;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {
|
|
81
|
+
throw new ProjectSlugConflictError(nextSlug);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
38
86
|
export async function getProjectById(id) {
|
|
39
87
|
const rows = await db().select().from(projects).where(eq(projects.id, id)).limit(1);
|
|
40
88
|
const row = rows[0];
|
|
@@ -47,12 +95,61 @@ export async function getProjectBySource(params) {
|
|
|
47
95
|
if (!row) return undefined;
|
|
48
96
|
return toProject(row);
|
|
49
97
|
}
|
|
98
|
+
export async function updateProjectSourceRepository(params) {
|
|
99
|
+
const executor = params.tx ?? db();
|
|
100
|
+
const rows = await executor.update(projects).set({
|
|
101
|
+
sourceRepositoryOwner: params.sourceRepositoryOwner,
|
|
102
|
+
sourceRepositoryName: params.sourceRepositoryName,
|
|
103
|
+
sourceDefaultBranch: params.sourceDefaultBranch,
|
|
104
|
+
updatedAt: new Date()
|
|
105
|
+
}).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.sourceConnectionId, params.sourceConnectionId), eq(projects.sourceExternalRepositoryId, params.sourceExternalRepositoryId))).returning();
|
|
106
|
+
const row = rows[0];
|
|
107
|
+
if (!row) return undefined;
|
|
108
|
+
return toProject(row);
|
|
109
|
+
}
|
|
50
110
|
export async function requireProjectForWorkspace(params) {
|
|
51
111
|
const project = await getProjectById(params.projectId);
|
|
52
112
|
if (!project) throw new ProjectNotFoundError(params.projectId);
|
|
53
113
|
if (project.workspaceId !== params.workspaceId) throw new ProjectNotFoundError(params.projectId);
|
|
54
114
|
return project;
|
|
55
115
|
}
|
|
116
|
+
export async function updateProjectSourceMetadata(params) {
|
|
117
|
+
await db().update(projects).set({
|
|
118
|
+
sourceConnectionId: params.sourceConnectionId,
|
|
119
|
+
sourceExternalRepositoryId: params.sourceExternalRepositoryId,
|
|
120
|
+
sourceRepositoryOwner: params.sourceRepositoryOwner,
|
|
121
|
+
sourceRepositoryName: params.sourceRepositoryName,
|
|
122
|
+
sourceDefaultBranch: params.sourceDefaultBranch,
|
|
123
|
+
updatedAt: new Date()
|
|
124
|
+
}).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.projectId)));
|
|
125
|
+
}
|
|
126
|
+
export async function resolveCheckoutTarget(params) {
|
|
127
|
+
const selection = {
|
|
128
|
+
projectId: projects.id,
|
|
129
|
+
connectionId: projects.sourceConnectionId,
|
|
130
|
+
externalRepositoryId: projects.sourceExternalRepositoryId
|
|
131
|
+
};
|
|
132
|
+
if ('project' in params.target) {
|
|
133
|
+
const [project] = await db().select(selection).from(projects).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.target.project))).limit(1);
|
|
134
|
+
return project;
|
|
135
|
+
}
|
|
136
|
+
const separator = params.target.repository.indexOf('/');
|
|
137
|
+
if (separator === 0 || separator === params.target.repository.length - 1) return undefined;
|
|
138
|
+
const repository = separator === -1 ? {
|
|
139
|
+
connectionId: params.target.connection ?? params.defaults.connectionId,
|
|
140
|
+
owner: params.defaults.owner,
|
|
141
|
+
name: params.target.repository
|
|
142
|
+
} : params.target.repository.indexOf('/', separator + 1) === -1 ? {
|
|
143
|
+
connectionId: params.target.connection ?? params.defaults.connectionId,
|
|
144
|
+
owner: params.target.repository.slice(0, separator),
|
|
145
|
+
name: params.target.repository.slice(separator + 1)
|
|
146
|
+
} : undefined;
|
|
147
|
+
if (repository === undefined) return undefined;
|
|
148
|
+
const matches = await db().select(selection).from(projects).where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.sourceConnectionId, repository.connectionId), sql`lower(${projects.sourceRepositoryOwner}) = lower(${repository.owner})`, sql`lower(${projects.sourceRepositoryName}) = lower(${repository.name})`)).limit(2);
|
|
149
|
+
// Owner/name isn't a unique key: renamed or reconnected repositories can leave
|
|
150
|
+
// more than one project row matching. Fail closed instead of picking arbitrarily.
|
|
151
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
152
|
+
}
|
|
56
153
|
export async function listProjects(params) {
|
|
57
154
|
const conditions = [
|
|
58
155
|
eq(projects.workspaceId, params.workspaceId)
|
|
@@ -60,7 +157,8 @@ export async function listProjects(params) {
|
|
|
60
157
|
const cursorCondition = cursorWhere(params.cursor);
|
|
61
158
|
if (cursorCondition) conditions.push(cursorCondition);
|
|
62
159
|
if (params.search) {
|
|
63
|
-
|
|
160
|
+
const searchPattern = `%${escapeIlikePattern(params.search)}%`;
|
|
161
|
+
conditions.push(or(ilike(projects.name, searchPattern), ilike(projects.slug, searchPattern)));
|
|
64
162
|
}
|
|
65
163
|
const rows = await db().select().from(projects).where(and(...conditions)).orderBy(desc(projects.createdAt), desc(projects.id)).limit(params.limit + 1);
|
|
66
164
|
const hasMore = rows.length > params.limit;
|
package/dist/db/projects.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
1
|
+
{"version":3,"sources":["../../src/db/projects.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {and, count, desc, eq, ilike, inArray, lt, or, type SQL, sql} from 'drizzle-orm';\nimport type {Project} from '#core/entities/project.js';\nimport {\n ProjectAlreadyExistsError,\n ProjectNotFoundError,\n ProjectSlugConflictError,\n} from '#core/errors.js';\nimport {recordProjectCreated} from '#metrics/instance.js';\nimport {db, type Executor} from './db.js';\nimport {projects, toProject} from './schema/projects.js';\n\ntype ProjectDatabase = ReturnType<typeof db>;\ntype ProjectTransaction = Parameters<Parameters<ProjectDatabase['transaction']>[0]>[0];\n\nexport interface CreateProjectParams {\n workspaceId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n name: string;\n slug: string;\n}\n\nexport interface UpdateProjectParams {\n projectId: string;\n name?: string | undefined;\n slug?: string | undefined;\n}\n\nexport interface UpdateProjectResult {\n project: Project;\n changed: boolean;\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\nconst PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT = 'projects_workspace_slug_unique';\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 let projectRow: typeof projects.$inferSelect | undefined;\n for (let attempt = 0; attempt < 2 && !projectRow; attempt += 1) {\n try {\n [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 slug: params.slug,\n })\n .onConflictDoNothing({\n target: [projects.sourceConnectionId, projects.sourceExternalRepositoryId],\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {\n throw new ProjectSlugConflictError(params.slug);\n }\n throw error;\n }\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 }\n }\n\n if (!projectRow) {\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 updateProject(\n params: UpdateProjectParams,\n options: {tx?: ProjectDatabase | ProjectTransaction | undefined} = {},\n): Promise<UpdateProjectResult | undefined> {\n const executor = options.tx ?? db();\n\n const [existingRow] = await executor\n .select()\n .from(projects)\n .where(eq(projects.id, params.projectId))\n .limit(1);\n if (!existingRow) return undefined;\n\n const nextName = params.name ?? existingRow.name;\n const nextSlug = params.slug ?? existingRow.slug;\n if (nextName === existingRow.name && nextSlug === existingRow.slug) {\n return {project: toProject(existingRow), changed: false};\n }\n\n try {\n const [row] = await executor\n .update(projects)\n .set({\n ...(params.name !== undefined ? {name: params.name} : {}),\n ...(params.slug !== undefined ? {slug: params.slug} : {}),\n updatedAt: new Date(),\n })\n .where(eq(projects.id, params.projectId))\n .returning();\n\n return row ? {project: toProject(row), changed: true} : undefined;\n } catch (error) {\n if (isUniqueViolation(error, PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT)) {\n throw new ProjectSlugConflictError(nextSlug);\n }\n throw error;\n }\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 interface UpdateProjectSourceRepositoryParams extends GetProjectBySourceParams {\n tx?: Executor;\n sourceRepositoryOwner: string;\n sourceRepositoryName: string;\n sourceDefaultBranch: string;\n}\n\nexport async function updateProjectSourceRepository(\n params: UpdateProjectSourceRepositoryParams,\n): Promise<Project | undefined> {\n const executor = params.tx ?? db();\n const rows = await executor\n .update(projects)\n .set({\n sourceRepositoryOwner: params.sourceRepositoryOwner,\n sourceRepositoryName: params.sourceRepositoryName,\n sourceDefaultBranch: params.sourceDefaultBranch,\n updatedAt: new Date(),\n })\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 .returning();\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 interface ResolveCheckoutTargetParams {\n workspaceId: string;\n defaults: {\n connectionId: string;\n owner: string;\n };\n target: {project: string} | {connection?: string | undefined; repository: string};\n}\n\nexport interface ResolvedCheckoutTarget {\n projectId: string;\n connectionId: string;\n externalRepositoryId: string;\n}\n\nexport interface UpdateProjectSourceMetadataParams {\n workspaceId: string;\n projectId: string;\n sourceConnectionId: string;\n sourceExternalRepositoryId: string;\n sourceRepositoryOwner: string;\n sourceRepositoryName: string;\n sourceDefaultBranch: string;\n}\n\nexport async function updateProjectSourceMetadata(\n params: UpdateProjectSourceMetadataParams,\n): Promise<void> {\n await db()\n .update(projects)\n .set({\n sourceConnectionId: params.sourceConnectionId,\n sourceExternalRepositoryId: params.sourceExternalRepositoryId,\n sourceRepositoryOwner: params.sourceRepositoryOwner,\n sourceRepositoryName: params.sourceRepositoryName,\n sourceDefaultBranch: params.sourceDefaultBranch,\n updatedAt: new Date(),\n })\n .where(and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.projectId)));\n}\n\nexport async function resolveCheckoutTarget(\n params: ResolveCheckoutTargetParams,\n): Promise<ResolvedCheckoutTarget | undefined> {\n const selection = {\n projectId: projects.id,\n connectionId: projects.sourceConnectionId,\n externalRepositoryId: projects.sourceExternalRepositoryId,\n };\n\n if ('project' in params.target) {\n const [project] = await db()\n .select(selection)\n .from(projects)\n .where(\n and(eq(projects.workspaceId, params.workspaceId), eq(projects.id, params.target.project)),\n )\n .limit(1);\n return project;\n }\n\n const separator = params.target.repository.indexOf('/');\n if (separator === 0 || separator === params.target.repository.length - 1) return undefined;\n\n const repository =\n separator === -1\n ? {\n connectionId: params.target.connection ?? params.defaults.connectionId,\n owner: params.defaults.owner,\n name: params.target.repository,\n }\n : params.target.repository.indexOf('/', separator + 1) === -1\n ? {\n connectionId: params.target.connection ?? params.defaults.connectionId,\n owner: params.target.repository.slice(0, separator),\n name: params.target.repository.slice(separator + 1),\n }\n : undefined;\n if (repository === undefined) return undefined;\n\n const matches = await db()\n .select(selection)\n .from(projects)\n .where(\n and(\n eq(projects.workspaceId, params.workspaceId),\n eq(projects.sourceConnectionId, repository.connectionId),\n sql`lower(${projects.sourceRepositoryOwner}) = lower(${repository.owner})`,\n sql`lower(${projects.sourceRepositoryName}) = lower(${repository.name})`,\n ),\n )\n .limit(2);\n\n // Owner/name isn't a unique key: renamed or reconnected repositories can leave\n // more than one project row matching. Fail closed instead of picking arbitrarily.\n return matches.length === 1 ? matches[0] : undefined;\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 const searchPattern = `%${escapeIlikePattern(params.search)}%`;\n conditions.push(\n or(ilike(projects.name, searchPattern), ilike(projects.slug, searchPattern)) as SQL,\n );\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":["isUniqueViolation","and","count","desc","eq","ilike","inArray","lt","or","sql","ProjectAlreadyExistsError","ProjectNotFoundError","ProjectSlugConflictError","recordProjectCreated","db","projects","toProject","escapeIlikePattern","value","replace","char","PROJECTS_WORKSPACE_SLUG_UNIQUE_CONSTRAINT","cursorWhere","cursor","undefined","createdAt","id","createProject","params","project","transaction","tx","projectRow","attempt","insert","values","workspaceId","sourceConnectionId","sourceExternalRepositoryId","name","slug","onConflictDoNothing","target","returning","error","conflict","select","from","where","limit","Error","updateProject","options","executor","existingRow","projectId","nextName","nextSlug","changed","row","update","set","updatedAt","Date","getProjectById","rows","getProjectBySource","updateProjectSourceRepository","sourceRepositoryOwner","sourceRepositoryName","sourceDefaultBranch","requireProjectForWorkspace","updateProjectSourceMetadata","resolveCheckoutTarget","selection","connectionId","externalRepositoryId","separator","repository","indexOf","length","connection","defaults","owner","slice","matches","listProjects","conditions","cursorCondition","push","search","searchPattern","orderBy","hasMore","pageRows","last","at","map","nextCursor","listAdminProjects","getProjectCount","getWorkspaceProjectCounts","workspaceIds","groupBy","countsByWorkspace","Map","Number","get"],"mappings":"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"}
|