@igstack/app-catalog-backend-core 0.3.1-alpha-20260405015231 → 0.4.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/dist/db/syncAppCatalog.d.mts +3 -5
- package/dist/db/syncAppCatalog.d.mts.map +1 -1
- package/dist/db/syncAppCatalog.mjs +49 -57
- package/dist/db/syncAppCatalog.mjs.map +1 -1
- package/dist/db/tableSyncMagazine.d.mts +3 -7
- package/dist/db/tableSyncMagazine.d.mts.map +1 -1
- package/dist/db/tableSyncMagazine.mjs +3 -7
- package/dist/db/tableSyncMagazine.mjs.map +1 -1
- package/dist/db/tableSyncPrismaAdapter.mjs.map +1 -1
- package/dist/generated/prisma/client.mjs.map +1 -1
- package/dist/generated/prisma/internal/class.d.mts +5 -17
- package/dist/generated/prisma/internal/class.d.mts.map +1 -1
- package/dist/generated/prisma/internal/class.mjs +4 -4
- package/dist/generated/prisma/internal/class.mjs.map +1 -1
- package/dist/generated/prisma/internal/prismaNamespace.d.mts +46 -132
- package/dist/generated/prisma/internal/prismaNamespace.d.mts.map +1 -1
- package/dist/generated/prisma/models/DbResource.d.mts +2433 -0
- package/dist/generated/prisma/models/DbResource.d.mts.map +1 -0
- package/dist/generated/prisma/models/SourceReference.d.mts +90 -90
- package/dist/generated/prisma/models/SourceReference.d.mts.map +1 -1
- package/dist/generated/prisma/models.d.mts +2 -3
- package/dist/index.d.mts +3 -4
- package/dist/modules/appCatalog/checkLinks.mjs +1 -1
- package/dist/modules/appCatalog/checkLinks.mjs.map +1 -1
- package/dist/modules/appCatalog/service.mjs +26 -34
- package/dist/modules/appCatalog/service.mjs.map +1 -1
- package/dist/modules/assets/screenshotRestController.mjs +2 -2
- package/dist/modules/assets/screenshotRestController.mjs.map +1 -1
- package/dist/modules/assets/syncAssets.mjs +4 -4
- package/dist/modules/assets/syncAssets.mjs.map +1 -1
- package/dist/modules/lighthouseKeeper/tools.mjs +1 -1
- package/dist/modules/lighthouseKeeper/tools.mjs.map +1 -1
- package/dist/server/controller.d.mts +2 -2
- package/dist/server/controller.mjs.map +1 -1
- package/dist/types/common/appCatalogTypes.d.mts +26 -9
- package/dist/types/common/appCatalogTypes.d.mts.map +1 -1
- package/dist/types/common/approvalMethodTypes.d.mts +5 -1
- package/dist/types/common/approvalMethodTypes.d.mts.map +1 -1
- package/package.json +3 -3
- package/prisma/schema.prisma +53 -62
- package/src/db/syncAppCatalog.ts +68 -73
- package/src/db/tableSyncMagazine.ts +3 -7
- package/src/db/tableSyncPrismaAdapter.ts +1 -1
- package/src/generated/prisma/browser.ts +2 -7
- package/src/generated/prisma/client.ts +2 -7
- package/src/generated/prisma/internal/class.ts +8 -18
- package/src/generated/prisma/internal/prismaNamespace.ts +43 -131
- package/src/generated/prisma/internal/prismaNamespaceBrowser.ts +7 -20
- package/src/generated/prisma/models/DbResource.ts +2701 -0
- package/src/generated/prisma/models/SourceReference.ts +89 -89
- package/src/generated/prisma/models.ts +1 -2
- package/src/index.ts +1 -1
- package/src/modules/appCatalog/checkLinks.ts +7 -7
- package/src/modules/appCatalog/service.ts +51 -62
- package/src/modules/assets/screenshotRestController.ts +2 -2
- package/src/modules/assets/screenshotRouter.ts +2 -2
- package/src/modules/assets/syncAssets.ts +4 -4
- package/src/modules/lighthouseKeeper/tools.ts +1 -1
- package/src/prisma-json-types.d.ts +8 -8
- package/src/server/controller.ts +2 -2
- package/src/types/common/appCatalogTypes.ts +28 -9
- package/src/types/common/approvalMethodTypes.ts +6 -0
- package/src/types/index.ts +0 -1
- package/dist/generated/prisma/models/DbAppForCatalog.d.mts +0 -1778
- package/dist/generated/prisma/models/DbAppForCatalog.d.mts.map +0 -1
- package/dist/generated/prisma/models/DbSubResource.d.mts +0 -1468
- package/dist/generated/prisma/models/DbSubResource.d.mts.map +0 -1
- package/dist/types/common/subResourceTypes.d.mts +0 -24
- package/dist/types/common/subResourceTypes.d.mts.map +0 -1
- package/src/generated/prisma/models/DbAppForCatalog.ts +0 -2014
- package/src/generated/prisma/models/DbSubResource.ts +0 -1692
- package/src/types/common/subResourceTypes.ts +0 -20
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ApprovalMethod } from "../types/common/approvalMethodTypes.mjs";
|
|
2
2
|
import { Group, Person } from "../types/common/personGroupTypes.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { AppForCatalog, GroupingTagDefinition } from "../types/common/appCatalogTypes.mjs";
|
|
3
|
+
import { GroupingTagDefinition, Resource } from "../types/common/appCatalogTypes.mjs";
|
|
5
4
|
|
|
6
5
|
//#region src/db/syncAppCatalog.d.ts
|
|
7
6
|
interface SyncAppCatalogResult {
|
|
@@ -16,15 +15,14 @@ interface SyncAppCatalogResult {
|
|
|
16
15
|
interface SyncAppCatalogOptions {
|
|
17
16
|
persons?: Person[];
|
|
18
17
|
groups?: Group[];
|
|
19
|
-
subResources?: SubResource[];
|
|
20
18
|
}
|
|
21
19
|
/**
|
|
22
20
|
* Syncs app catalog data to the database using table sync.
|
|
23
|
-
* This will create new
|
|
21
|
+
* This will create new resources, update existing ones, and delete any that are no longer in the input.
|
|
24
22
|
*
|
|
25
23
|
* Note: Call connectDb() before and disconnectDb() after if running in a script.
|
|
26
24
|
*/
|
|
27
|
-
declare function syncAppCatalog(
|
|
25
|
+
declare function syncAppCatalog(resources: Resource[], tagsDefinitions: GroupingTagDefinition[], approvalMethods: ApprovalMethod[], screenshotsPath?: string, options?: SyncAppCatalogOptions): Promise<SyncAppCatalogResult>;
|
|
28
26
|
//#endregion
|
|
29
27
|
export { SyncAppCatalogResult, syncAppCatalog };
|
|
30
28
|
//# sourceMappingURL=syncAppCatalog.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncAppCatalog.d.mts","names":[],"sources":["../../src/db/syncAppCatalog.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"syncAppCatalog.d.mts","names":[],"sources":["../../src/db/syncAppCatalog.ts"],"mappings":";;;;;UAeiB,oBAAA;EACf,OAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA;AAAA;;;;UAuJe,qBAAA;EACf,OAAA,GAAU,MAAA;EACV,MAAA,GAAS,KAAA;AAAA;AAFX;;;;;;AAAA,iBAWsB,cAAA,CACpB,SAAA,EAAW,QAAA,IACX,eAAA,EAAiB,qBAAA,IACjB,eAAA,EAAiB,cAAA,IACjB,eAAA,WACA,OAAA,GAAU,qBAAA,GACT,OAAA,CAAQ,oBAAA"}
|
|
@@ -41,10 +41,10 @@ async function syncAppAssets(appSlug, appPath, prisma) {
|
|
|
41
41
|
iconName: (await processAssetDirectory(`${appPath}/icons`, appSlug, "icon", prisma)).length > 0 ? `${appSlug}-icon` : null
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
async function syncAssetsFromFileSystem(
|
|
44
|
+
async function syncAssetsFromFileSystem(resources, allAppsAssetsPath) {
|
|
45
45
|
const appDirectories = await readdir(allAppsAssetsPath);
|
|
46
46
|
const prisma = getDbClient();
|
|
47
|
-
const bySlug = group(
|
|
47
|
+
const bySlug = group(resources, (a) => a.slug);
|
|
48
48
|
for (const appDirName of appDirectories) {
|
|
49
49
|
try {
|
|
50
50
|
if (!(await stat(`${allAppsAssetsPath}/${appDirName}`)).isDirectory()) continue;
|
|
@@ -59,7 +59,7 @@ async function syncAssetsFromFileSystem(apps, allAppsAssetsPath) {
|
|
|
59
59
|
const updateData = {};
|
|
60
60
|
if (screenshotIds.length > 0) updateData.screenshotIds = screenshotIds;
|
|
61
61
|
if (iconName !== null) updateData.iconName = iconName;
|
|
62
|
-
if (Object.keys(updateData).length > 0) await prisma.
|
|
62
|
+
if (Object.keys(updateData).length > 0) await prisma.dbResource.update({
|
|
63
63
|
where: { slug: appSlug },
|
|
64
64
|
data: updateData
|
|
65
65
|
});
|
|
@@ -71,11 +71,11 @@ async function syncAssetsFromFileSystem(apps, allAppsAssetsPath) {
|
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
73
|
* Syncs app catalog data to the database using table sync.
|
|
74
|
-
* This will create new
|
|
74
|
+
* This will create new resources, update existing ones, and delete any that are no longer in the input.
|
|
75
75
|
*
|
|
76
76
|
* Note: Call connectDb() before and disconnectDb() after if running in a script.
|
|
77
77
|
*/
|
|
78
|
-
async function syncAppCatalog(
|
|
78
|
+
async function syncAppCatalog(resources, tagsDefinitions, approvalMethods, screenshotsPath, options) {
|
|
79
79
|
try {
|
|
80
80
|
const prisma = getDbClient();
|
|
81
81
|
if (options?.persons) {
|
|
@@ -115,14 +115,14 @@ async function syncAppCatalog(apps, tagsDefinitions, approvalMethods, sreenshots
|
|
|
115
115
|
}).sync(approvalMethods);
|
|
116
116
|
const sync = tableSyncPrisma({
|
|
117
117
|
prisma,
|
|
118
|
-
...TABLE_SYNC_MAGAZINE.
|
|
118
|
+
...TABLE_SYNC_MAGAZINE.DbResource
|
|
119
119
|
});
|
|
120
120
|
await tableSyncPrisma({
|
|
121
121
|
prisma,
|
|
122
122
|
...TABLE_SYNC_MAGAZINE.DbAppTagDefinition
|
|
123
123
|
}).sync(tagsDefinitions);
|
|
124
124
|
const uniqueSourceSlugs = /* @__PURE__ */ new Set();
|
|
125
|
-
for (const
|
|
125
|
+
for (const resource of resources) for (const source of resource.sources ?? []) {
|
|
126
126
|
const sourceSlug = parseSourceSlug(typeof source === "string" ? source : source.url);
|
|
127
127
|
uniqueSourceSlugs.add(sourceSlug);
|
|
128
128
|
}
|
|
@@ -134,45 +134,57 @@ async function syncAppCatalog(apps, tagsDefinitions, approvalMethods, sreenshots
|
|
|
134
134
|
prisma,
|
|
135
135
|
...TABLE_SYNC_MAGAZINE.Source
|
|
136
136
|
}).sync(sources);
|
|
137
|
-
const
|
|
137
|
+
const sortedResources = [...resources].sort((a, b) => {
|
|
138
|
+
return (a.parentSlug ? 1 : 0) - (b.parentSlug ? 1 : 0);
|
|
139
|
+
});
|
|
140
|
+
const dbResources = sortedResources.map((resource) => {
|
|
138
141
|
return {
|
|
139
|
-
slug:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
142
|
+
slug: resource.slug || resource.displayName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""),
|
|
143
|
+
type: resource.type ?? "application",
|
|
144
|
+
displayName: resource.displayName,
|
|
145
|
+
abbreviation: resource.abbreviation ?? null,
|
|
146
|
+
nicknames: resource.nicknames ?? [],
|
|
147
|
+
description: resource.description,
|
|
148
|
+
teams: resource.teams ?? [],
|
|
149
|
+
accessRequest: resource.accessRequest ?? null,
|
|
150
|
+
notes: resource.notes ?? null,
|
|
151
|
+
tags: resource.tags ?? [],
|
|
152
|
+
appUrl: resource.appUrl ?? null,
|
|
153
|
+
links: resource.links ?? null,
|
|
154
|
+
iconName: resource.iconName ?? null,
|
|
155
|
+
screenshotIds: resource.screenshotIds ?? [],
|
|
156
|
+
deprecated: resource.deprecated ?? null,
|
|
157
|
+
aiPrompt: resource.aiPrompt ?? null,
|
|
158
|
+
urlIssues: resource.urlIssues ?? [],
|
|
159
|
+
tiers: resource.tiers ?? null,
|
|
160
|
+
parentSlug: resource.parentSlug ?? null,
|
|
161
|
+
tier: resource.tier ?? null,
|
|
162
|
+
familySlug: resource.familySlug ?? null,
|
|
163
|
+
aliases: resource.aliases ?? [],
|
|
164
|
+
ownerPersonSlug: resource.ownerPersonSlug ?? null,
|
|
165
|
+
accessMaintainerGroupSlugs: resource.accessMaintainerGroupSlugs ?? [],
|
|
166
|
+
accessComments: resource.accessComments ?? null,
|
|
167
|
+
extra: resource.extra ?? null
|
|
156
168
|
};
|
|
157
169
|
});
|
|
158
|
-
const result = await sync.sync(
|
|
159
|
-
const slugs =
|
|
160
|
-
const
|
|
170
|
+
const result = await sync.sync(dbResources);
|
|
171
|
+
const slugs = dbResources.map((a) => a.slug);
|
|
172
|
+
const resourceRows = await prisma.dbResource.findMany({
|
|
161
173
|
where: { slug: { in: slugs } },
|
|
162
174
|
select: {
|
|
163
175
|
slug: true,
|
|
164
176
|
id: true
|
|
165
177
|
}
|
|
166
178
|
});
|
|
167
|
-
const slugToId = Object.fromEntries(
|
|
168
|
-
const allSourceRefs =
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
if (!
|
|
172
|
-
return (
|
|
179
|
+
const slugToId = Object.fromEntries(resourceRows.map((r) => [r.slug, r.id]));
|
|
180
|
+
const allSourceRefs = sortedResources.flatMap((resource) => {
|
|
181
|
+
const resourceSlug = resource.slug || resource.displayName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
182
|
+
const resourceId = slugToId[resourceSlug];
|
|
183
|
+
if (!resourceId) throw new Error(`Resource '${resourceSlug}' has no id after sync. Existing slugs: ${Object.keys(slugToId).join(", ")}`);
|
|
184
|
+
return (resource.sources ?? []).map((source) => {
|
|
173
185
|
const url = typeof source === "string" ? source : source.url;
|
|
174
186
|
return {
|
|
175
|
-
|
|
187
|
+
resourceId,
|
|
176
188
|
sourceSlug: parseSourceSlug(url),
|
|
177
189
|
url,
|
|
178
190
|
parseDate: null,
|
|
@@ -185,31 +197,11 @@ async function syncAppCatalog(apps, tagsDefinitions, approvalMethods, sreenshots
|
|
|
185
197
|
prisma,
|
|
186
198
|
...TABLE_SYNC_MAGAZINE.SourceReference
|
|
187
199
|
}).sync(allSourceRefs);
|
|
188
|
-
if (options?.subResources) {
|
|
189
|
-
const dbSubResources = options.subResources.map((sr) => ({
|
|
190
|
-
slug: sr.slug,
|
|
191
|
-
displayName: sr.displayName,
|
|
192
|
-
description: sr.description ?? null,
|
|
193
|
-
appSlug: sr.appSlug,
|
|
194
|
-
familySlug: sr.familySlug ?? null,
|
|
195
|
-
tierSlug: sr.tierSlug ?? null,
|
|
196
|
-
aliases: sr.aliases,
|
|
197
|
-
ownerPersonSlug: sr.ownerPersonSlug ?? null,
|
|
198
|
-
accessMaintainerGroupSlugs: sr.accessMaintainerGroupSlugs,
|
|
199
|
-
accessRequest: sr.accessRequest ?? null,
|
|
200
|
-
accessComments: sr.accessComments ?? null,
|
|
201
|
-
extra: sr.extra ?? null
|
|
202
|
-
}));
|
|
203
|
-
await tableSyncPrisma({
|
|
204
|
-
prisma,
|
|
205
|
-
...TABLE_SYNC_MAGAZINE.DbSubResource
|
|
206
|
-
}).sync(dbSubResources);
|
|
207
|
-
}
|
|
208
200
|
const actual = result.getActual();
|
|
209
|
-
if (
|
|
201
|
+
if (screenshotsPath) await syncAssetsFromFileSystem(resources, screenshotsPath);
|
|
210
202
|
else console.warn("Do not sync screenhots");
|
|
211
203
|
return {
|
|
212
|
-
created: actual.length -
|
|
204
|
+
created: actual.length - resources.length + (resources.length - actual.length),
|
|
213
205
|
updated: 0,
|
|
214
206
|
deleted: 0,
|
|
215
207
|
total: actual.length
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncAppCatalog.mjs","names":[],"sources":["../../src/db/syncAppCatalog.ts"],"sourcesContent":["import type {\n AppForCatalog,\n GroupingTagDefinition,\n} from '../types/common/appCatalogTypes'\nimport { getDbClient } from './client'\nimport { TABLE_SYNC_MAGAZINE } from './tableSyncMagazine'\nimport { tableSyncPrisma } from './tableSyncPrismaAdapter'\nimport { readFile, readdir, stat } from 'node:fs/promises'\nimport { group } from 'radashi'\nimport { upsertAsset } from '../modules/assets/upsertAsset'\nimport type { ApprovalMethod, Group, Person, SubResource } from '../types'\nimport type { PrismaClient } from '../generated/prisma/client'\nimport { naturalSort } from '../utils/naturalSort'\nimport { parseSourceSlug } from '../utils/parseSourceSlug'\n\nexport interface SyncAppCatalogResult {\n created: number\n updated: number\n deleted: number\n total: number\n}\n\ninterface AssetSyncResult {\n screenshotIds: string[]\n iconName: string | null\n}\n\nfunction isFileNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n 'code' in error &&\n (error as NodeJS.ErrnoException).code === 'ENOENT'\n )\n}\n\nasync function processAssetDirectory(\n dirPath: string,\n appSlug: string,\n assetType: 'screenshot' | 'icon',\n prisma: PrismaClient,\n): Promise<string[]> {\n try {\n const files = await readdir(dirPath)\n const sortedFiles = naturalSort(files)\n const assetIds: string[] = []\n\n for (let i = 0; i < sortedFiles.length; i++) {\n const fileName = sortedFiles[i]\n if (!fileName) continue\n\n const assetName =\n assetType === 'screenshot'\n ? `${appSlug}-screenshot-${i + 1}`\n : `${appSlug}-icon`\n\n const id = await upsertAsset({\n prisma,\n buffer: await readFile(`${dirPath}/${fileName}`),\n originalFilename: fileName,\n name: assetName,\n assetType,\n })\n assetIds.push(id)\n\n // For icons, only process the first file\n if (assetType === 'icon') {\n break\n }\n }\n\n return assetIds\n } catch (error: unknown) {\n if (isFileNotFoundError(error)) {\n return []\n }\n throw error\n }\n}\n\nasync function syncAppAssets(\n appSlug: string,\n appPath: string,\n prisma: PrismaClient,\n): Promise<AssetSyncResult> {\n const screenshotIds = await processAssetDirectory(\n `${appPath}/screenshots`,\n appSlug,\n 'screenshot',\n prisma,\n )\n\n const iconIds = await processAssetDirectory(\n `${appPath}/icons`,\n appSlug,\n 'icon',\n prisma,\n )\n\n return {\n screenshotIds,\n iconName: iconIds.length > 0 ? `${appSlug}-icon` : null,\n }\n}\n\nasync function syncAssetsFromFileSystem(\n apps: AppForCatalog[],\n allAppsAssetsPath: string,\n) {\n const appDirectories = await readdir(allAppsAssetsPath)\n const prisma = getDbClient()\n const bySlug = group(apps, (a) => a.slug)\n\n for (const appDirName of appDirectories) {\n try {\n const stats = await stat(`${allAppsAssetsPath}/${appDirName}`)\n if (!stats.isDirectory()) {\n continue\n }\n } catch (error: unknown) {\n if (isFileNotFoundError(error)) {\n continue\n }\n throw error\n }\n\n const appSlug = appDirName\n if (!bySlug[appSlug]) {\n throw new Error(\n `App '${appSlug}' does not exist in the app catalog. Existing apps: ${Object.keys(bySlug).join(', ')}`,\n )\n }\n\n try {\n const { screenshotIds, iconName } = await syncAppAssets(\n appSlug,\n `${allAppsAssetsPath}/${appDirName}`,\n prisma,\n )\n\n const updateData: {\n screenshotIds?: string[]\n iconName?: string | null\n } = {}\n\n if (screenshotIds.length > 0) {\n updateData.screenshotIds = screenshotIds\n }\n if (iconName !== null) {\n updateData.iconName = iconName\n }\n\n if (Object.keys(updateData).length > 0) {\n await prisma.dbAppForCatalog.update({\n where: { slug: appSlug },\n data: updateData,\n })\n }\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n throw new Error(\n `Error while upserting assets for app '${appSlug}': ${errorMessage}`,\n )\n }\n }\n}\n\n/**\n * Optional data to sync alongside the core app catalog.\n */\nexport interface SyncAppCatalogOptions {\n persons?: Person[]\n groups?: Group[]\n subResources?: SubResource[]\n}\n\n/**\n * Syncs app catalog data to the database using table sync.\n * This will create new apps, update existing ones, and delete any that are no longer in the input.\n *\n * Note: Call connectDb() before and disconnectDb() after if running in a script.\n */\nexport async function syncAppCatalog(\n apps: AppForCatalog[],\n tagsDefinitions: GroupingTagDefinition[],\n approvalMethods: ApprovalMethod[],\n sreenshotsPath?: string,\n options?: SyncAppCatalogOptions,\n): Promise<SyncAppCatalogResult> {\n try {\n const prisma = getDbClient()\n\n // Sync Persons first (groups depend on persons via memberships)\n if (options?.persons) {\n const dbPersons = options.persons.map((p) => ({\n slug: p.slug,\n firstName: p.firstName,\n lastName: p.lastName,\n email: p.email ?? null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbPerson,\n }).sync(dbPersons)\n }\n\n // Sync Groups (without memberships first)\n if (options?.groups) {\n const dbGroups = options.groups.map((g) => ({\n slug: g.slug,\n displayName: g.displayName ?? null,\n email: g.email ?? null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbGroup,\n }).sync(dbGroups)\n\n // Now sync GroupMemberships\n const allMemberships = options.groups.flatMap((g) =>\n g.memberSlugs.map((personSlug) => ({\n groupSlug: g.slug,\n personSlug,\n })),\n )\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbGroupMembership,\n }).sync(allMemberships)\n }\n\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbApprovalMethod,\n }).sync(approvalMethods)\n\n const sync = tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbAppForCatalog,\n })\n\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbAppTagDefinition,\n }).sync(tagsDefinitions)\n\n // Collect all unique source slugs for sync\n const uniqueSourceSlugs = new Set<string>()\n for (const app of apps) {\n for (const source of app.sources ?? []) {\n const url = typeof source === 'string' ? source : source.url\n const sourceSlug = parseSourceSlug(url)\n uniqueSourceSlugs.add(sourceSlug)\n }\n }\n\n // Sync Source entries using tableSyncPrisma\n const sources = Array.from(uniqueSourceSlugs).map((slug) => ({\n slug,\n userPrompt: null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.Source,\n }).sync(sources)\n\n // Transform AppForCatalog to DbAppForCatalog format (scalar fields only)\n const dbApps = apps.map((app) => {\n const slug =\n app.slug ||\n app.displayName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n\n return {\n slug,\n displayName: app.displayName,\n abbreviation: app.abbreviation ?? null,\n nicknames: app.nicknames ?? [],\n description: app.description,\n teams: app.teams ?? [],\n accessRequest: app.accessRequest ?? null,\n notes: app.notes ?? null,\n tags: app.tags ?? [],\n appUrl: app.appUrl ?? null,\n links: app.links ?? null,\n iconName: app.iconName ?? null,\n screenshotIds: app.screenshotIds ?? [],\n deprecated: app.deprecated ?? null,\n aiPrompt: app.aiPrompt ?? null,\n urlIssues: app.urlIssues ?? [],\n tiers: app.tiers ?? null,\n }\n })\n\n // Sync apps first\n const result = await sync.sync(dbApps)\n\n // Resolve slug -> id for synced apps so SourceReference can reference by appId\n const slugs = dbApps.map((a) => a.slug)\n const appRows = await prisma.dbAppForCatalog.findMany({\n where: { slug: { in: slugs } },\n select: { slug: true, id: true },\n })\n const slugToId = Object.fromEntries(appRows.map((r) => [r.slug, r.id]))\n\n // Build allSourceRefs with appId (slug already resolved to id)\n const allSourceRefs = apps.flatMap((app) => {\n const appSlug =\n app.slug ||\n app.displayName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n const appId = slugToId[appSlug]\n if (!appId) {\n throw new Error(\n `App '${appSlug}' has no id after sync. Existing slugs: ${Object.keys(slugToId).join(', ')}`,\n )\n }\n\n return (app.sources ?? []).map((source) => {\n const url = typeof source === 'string' ? source : source.url\n const sourceSlug = parseSourceSlug(url)\n return {\n appId,\n sourceSlug,\n url,\n parseDate: null,\n excerpts: [],\n userPrompt: null,\n }\n })\n })\n\n // Then sync all SourceReferences (with set semantics: old ones deleted, new ones created)\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.SourceReference,\n }).sync(allSourceRefs)\n\n // Sync SubResources (after apps, since they reference appSlug)\n if (options?.subResources) {\n const dbSubResources = options.subResources.map((sr) => ({\n slug: sr.slug,\n displayName: sr.displayName,\n description: sr.description ?? null,\n appSlug: sr.appSlug,\n familySlug: sr.familySlug ?? null,\n tierSlug: sr.tierSlug ?? null,\n aliases: sr.aliases,\n ownerPersonSlug: sr.ownerPersonSlug ?? null,\n accessMaintainerGroupSlugs: sr.accessMaintainerGroupSlugs,\n accessRequest: sr.accessRequest ?? null,\n accessComments: sr.accessComments ?? null,\n extra: sr.extra ?? null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbSubResource,\n }).sync(dbSubResources)\n }\n\n // Get actual synced data to calculate stats\n const actual = result.getActual()\n\n if (sreenshotsPath) {\n await syncAssetsFromFileSystem(apps, sreenshotsPath)\n } else {\n console.warn('Do not sync screenhots')\n }\n\n return {\n created: actual.length - apps.length + (apps.length - actual.length),\n updated: 0, // TableSync doesn't expose this directly\n deleted: 0, // TableSync doesn't expose this directly\n total: actual.length,\n }\n } catch (error) {\n // Wrap error with context\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorStack = error instanceof Error ? error.stack : undefined\n\n throw new Error(\n `Error syncing app catalog: ${errorMessage}\\n\\nDetails:\\n${errorStack || 'No stack trace available'}`,\n )\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncAppCatalog.mjs","names":[],"sources":["../../src/db/syncAppCatalog.ts"],"sourcesContent":["import type {\n GroupingTagDefinition,\n Resource,\n} from '../types/common/appCatalogTypes'\nimport { getDbClient } from './client'\nimport { TABLE_SYNC_MAGAZINE } from './tableSyncMagazine'\nimport { tableSyncPrisma } from './tableSyncPrismaAdapter'\nimport { readFile, readdir, stat } from 'node:fs/promises'\nimport { group } from 'radashi'\nimport { upsertAsset } from '../modules/assets/upsertAsset'\nimport type { ApprovalMethod, Group, Person } from '../types'\nimport type { PrismaClient } from '../generated/prisma/client'\nimport { naturalSort } from '../utils/naturalSort'\nimport { parseSourceSlug } from '../utils/parseSourceSlug'\n\nexport interface SyncAppCatalogResult {\n created: number\n updated: number\n deleted: number\n total: number\n}\n\ninterface AssetSyncResult {\n screenshotIds: string[]\n iconName: string | null\n}\n\nfunction isFileNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n 'code' in error &&\n (error as NodeJS.ErrnoException).code === 'ENOENT'\n )\n}\n\nasync function processAssetDirectory(\n dirPath: string,\n appSlug: string,\n assetType: 'screenshot' | 'icon',\n prisma: PrismaClient,\n): Promise<string[]> {\n try {\n const files = await readdir(dirPath)\n const sortedFiles = naturalSort(files)\n const assetIds: string[] = []\n\n for (let i = 0; i < sortedFiles.length; i++) {\n const fileName = sortedFiles[i]\n if (!fileName) continue\n\n const assetName =\n assetType === 'screenshot'\n ? `${appSlug}-screenshot-${i + 1}`\n : `${appSlug}-icon`\n\n const id = await upsertAsset({\n prisma,\n buffer: await readFile(`${dirPath}/${fileName}`),\n originalFilename: fileName,\n name: assetName,\n assetType,\n })\n assetIds.push(id)\n\n // For icons, only process the first file\n if (assetType === 'icon') {\n break\n }\n }\n\n return assetIds\n } catch (error: unknown) {\n if (isFileNotFoundError(error)) {\n return []\n }\n throw error\n }\n}\n\nasync function syncAppAssets(\n appSlug: string,\n appPath: string,\n prisma: PrismaClient,\n): Promise<AssetSyncResult> {\n const screenshotIds = await processAssetDirectory(\n `${appPath}/screenshots`,\n appSlug,\n 'screenshot',\n prisma,\n )\n\n const iconIds = await processAssetDirectory(\n `${appPath}/icons`,\n appSlug,\n 'icon',\n prisma,\n )\n\n return {\n screenshotIds,\n iconName: iconIds.length > 0 ? `${appSlug}-icon` : null,\n }\n}\n\nasync function syncAssetsFromFileSystem(\n resources: Resource[],\n allAppsAssetsPath: string,\n) {\n const appDirectories = await readdir(allAppsAssetsPath)\n const prisma = getDbClient()\n const bySlug = group(resources, (a) => a.slug)\n\n for (const appDirName of appDirectories) {\n try {\n const stats = await stat(`${allAppsAssetsPath}/${appDirName}`)\n if (!stats.isDirectory()) {\n continue\n }\n } catch (error: unknown) {\n if (isFileNotFoundError(error)) {\n continue\n }\n throw error\n }\n\n const appSlug = appDirName\n if (!bySlug[appSlug]) {\n throw new Error(\n `App '${appSlug}' does not exist in the app catalog. Existing apps: ${Object.keys(bySlug).join(', ')}`,\n )\n }\n\n try {\n const { screenshotIds, iconName } = await syncAppAssets(\n appSlug,\n `${allAppsAssetsPath}/${appDirName}`,\n prisma,\n )\n\n const updateData: {\n screenshotIds?: string[]\n iconName?: string | null\n } = {}\n\n if (screenshotIds.length > 0) {\n updateData.screenshotIds = screenshotIds\n }\n if (iconName !== null) {\n updateData.iconName = iconName\n }\n\n if (Object.keys(updateData).length > 0) {\n await prisma.dbResource.update({\n where: { slug: appSlug },\n data: updateData,\n })\n }\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n throw new Error(\n `Error while upserting assets for app '${appSlug}': ${errorMessage}`,\n )\n }\n }\n}\n\n/**\n * Optional data to sync alongside the core app catalog.\n */\nexport interface SyncAppCatalogOptions {\n persons?: Person[]\n groups?: Group[]\n}\n\n/**\n * Syncs app catalog data to the database using table sync.\n * This will create new resources, update existing ones, and delete any that are no longer in the input.\n *\n * Note: Call connectDb() before and disconnectDb() after if running in a script.\n */\nexport async function syncAppCatalog(\n resources: Resource[],\n tagsDefinitions: GroupingTagDefinition[],\n approvalMethods: ApprovalMethod[],\n screenshotsPath?: string,\n options?: SyncAppCatalogOptions,\n): Promise<SyncAppCatalogResult> {\n try {\n const prisma = getDbClient()\n\n // Sync Persons first (groups depend on persons via memberships)\n if (options?.persons) {\n const dbPersons = options.persons.map((p) => ({\n slug: p.slug,\n firstName: p.firstName,\n lastName: p.lastName,\n email: p.email ?? null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbPerson,\n }).sync(dbPersons)\n }\n\n // Sync Groups (without memberships first)\n if (options?.groups) {\n const dbGroups = options.groups.map((g) => ({\n slug: g.slug,\n displayName: g.displayName ?? null,\n email: g.email ?? null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbGroup,\n }).sync(dbGroups)\n\n // Now sync GroupMemberships\n const allMemberships = options.groups.flatMap((g) =>\n g.memberSlugs.map((personSlug) => ({\n groupSlug: g.slug,\n personSlug,\n })),\n )\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbGroupMembership,\n }).sync(allMemberships)\n }\n\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbApprovalMethod,\n }).sync(approvalMethods)\n\n const sync = tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbResource,\n })\n\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.DbAppTagDefinition,\n }).sync(tagsDefinitions)\n\n // Collect all unique source slugs for sync\n const uniqueSourceSlugs = new Set<string>()\n for (const resource of resources) {\n for (const source of resource.sources ?? []) {\n const url = typeof source === 'string' ? source : source.url\n const sourceSlug = parseSourceSlug(url)\n uniqueSourceSlugs.add(sourceSlug)\n }\n }\n\n // Sync Source entries using tableSyncPrisma\n const sources = Array.from(uniqueSourceSlugs).map((slug) => ({\n slug,\n userPrompt: null,\n }))\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.Source,\n }).sync(sources)\n\n // Sort resources: parents first (no parentSlug), then children — for FK integrity\n const sortedResources = [...resources].sort((a, b) => {\n const aIsChild = a.parentSlug ? 1 : 0\n const bIsChild = b.parentSlug ? 1 : 0\n return aIsChild - bIsChild\n })\n\n // Transform Resource to DbResource format (scalar fields only)\n const dbResources = sortedResources.map((resource) => {\n const slug =\n resource.slug ||\n resource.displayName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n\n return {\n slug,\n type: resource.type ?? 'application',\n displayName: resource.displayName,\n abbreviation: resource.abbreviation ?? null,\n nicknames: resource.nicknames ?? [],\n description: resource.description,\n teams: resource.teams ?? [],\n accessRequest: resource.accessRequest ?? null,\n notes: resource.notes ?? null,\n tags: resource.tags ?? [],\n appUrl: resource.appUrl ?? null,\n links: resource.links ?? null,\n iconName: resource.iconName ?? null,\n screenshotIds: resource.screenshotIds ?? [],\n deprecated: resource.deprecated ?? null,\n aiPrompt: resource.aiPrompt ?? null,\n urlIssues: resource.urlIssues ?? [],\n tiers: resource.tiers ?? null,\n // Fields from former SubResource\n parentSlug: resource.parentSlug ?? null,\n tier: resource.tier ?? null,\n familySlug: resource.familySlug ?? null,\n aliases: resource.aliases ?? [],\n ownerPersonSlug: resource.ownerPersonSlug ?? null,\n accessMaintainerGroupSlugs: resource.accessMaintainerGroupSlugs ?? [],\n accessComments: resource.accessComments ?? null,\n extra: resource.extra ?? null,\n }\n })\n\n // Sync resources\n const result = await sync.sync(dbResources)\n\n // Resolve slug -> id for synced resources so SourceReference can reference by resourceId\n const slugs = dbResources.map((a) => a.slug)\n const resourceRows = await prisma.dbResource.findMany({\n where: { slug: { in: slugs } },\n select: { slug: true, id: true },\n })\n const slugToId = Object.fromEntries(resourceRows.map((r) => [r.slug, r.id]))\n\n // Build allSourceRefs with resourceId (slug already resolved to id)\n const allSourceRefs = sortedResources.flatMap((resource) => {\n const resourceSlug =\n resource.slug ||\n resource.displayName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n const resourceId = slugToId[resourceSlug]\n if (!resourceId) {\n throw new Error(\n `Resource '${resourceSlug}' has no id after sync. Existing slugs: ${Object.keys(slugToId).join(', ')}`,\n )\n }\n\n return (resource.sources ?? []).map((source) => {\n const url = typeof source === 'string' ? source : source.url\n const sourceSlug = parseSourceSlug(url)\n return {\n resourceId,\n sourceSlug,\n url,\n parseDate: null,\n excerpts: [],\n userPrompt: null,\n }\n })\n })\n\n // Then sync all SourceReferences (with set semantics: old ones deleted, new ones created)\n await tableSyncPrisma({\n prisma,\n ...TABLE_SYNC_MAGAZINE.SourceReference,\n }).sync(allSourceRefs)\n\n // Get actual synced data to calculate stats\n const actual = result.getActual()\n\n if (screenshotsPath) {\n await syncAssetsFromFileSystem(resources, screenshotsPath)\n } else {\n console.warn('Do not sync screenhots')\n }\n\n return {\n created:\n actual.length - resources.length + (resources.length - actual.length),\n updated: 0, // TableSync doesn't expose this directly\n deleted: 0, // TableSync doesn't expose this directly\n total: actual.length,\n }\n } catch (error) {\n // Wrap error with context\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorStack = error instanceof Error ? error.stack : undefined\n\n throw new Error(\n `Error syncing app catalog: ${errorMessage}\\n\\nDetails:\\n${errorStack || 'No stack trace available'}`,\n )\n }\n}\n"],"mappings":"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"}
|
|
@@ -14,8 +14,8 @@ declare const TABLE_SYNC_MAGAZINE: {
|
|
|
14
14
|
readonly prismaModelName: "DbGroupMembership";
|
|
15
15
|
readonly uniqColumns: ["groupSlug", "personSlug"];
|
|
16
16
|
};
|
|
17
|
-
readonly
|
|
18
|
-
readonly prismaModelName: "
|
|
17
|
+
readonly DbResource: {
|
|
18
|
+
readonly prismaModelName: "DbResource";
|
|
19
19
|
readonly uniqColumns: ["slug"];
|
|
20
20
|
};
|
|
21
21
|
readonly DbAppTagDefinition: {
|
|
@@ -32,13 +32,9 @@ declare const TABLE_SYNC_MAGAZINE: {
|
|
|
32
32
|
readonly prismaModelName: "Source";
|
|
33
33
|
readonly uniqColumns: ["slug"];
|
|
34
34
|
};
|
|
35
|
-
readonly DbSubResource: {
|
|
36
|
-
readonly prismaModelName: "DbSubResource";
|
|
37
|
-
readonly uniqColumns: ["slug"];
|
|
38
|
-
};
|
|
39
35
|
readonly SourceReference: {
|
|
40
36
|
readonly prismaModelName: "SourceReference";
|
|
41
|
-
readonly uniqColumns: ["
|
|
37
|
+
readonly uniqColumns: ["resourceId", "url"];
|
|
42
38
|
};
|
|
43
39
|
};
|
|
44
40
|
type TableSyncMagazine = typeof TABLE_SYNC_MAGAZINE;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tableSyncMagazine.d.mts","names":[],"sources":["../../src/db/tableSyncMagazine.ts"],"mappings":";cAca,mBAAA;EAAA
|
|
1
|
+
{"version":3,"file":"tableSyncMagazine.d.mts","names":[],"sources":["../../src/db/tableSyncMagazine.ts"],"mappings":";cAca,mBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAuCD,iBAAA,UAA2B,mBAAA;AAAA,KAC3B,6BAAA,SAAsC,iBAAA"}
|
|
@@ -14,8 +14,8 @@ const TABLE_SYNC_MAGAZINE = {
|
|
|
14
14
|
prismaModelName: "DbGroupMembership",
|
|
15
15
|
uniqColumns: ["groupSlug", "personSlug"]
|
|
16
16
|
},
|
|
17
|
-
|
|
18
|
-
prismaModelName: "
|
|
17
|
+
DbResource: {
|
|
18
|
+
prismaModelName: "DbResource",
|
|
19
19
|
uniqColumns: ["slug"]
|
|
20
20
|
},
|
|
21
21
|
DbAppTagDefinition: {
|
|
@@ -32,13 +32,9 @@ const TABLE_SYNC_MAGAZINE = {
|
|
|
32
32
|
prismaModelName: "Source",
|
|
33
33
|
uniqColumns: ["slug"]
|
|
34
34
|
},
|
|
35
|
-
DbSubResource: {
|
|
36
|
-
prismaModelName: "DbSubResource",
|
|
37
|
-
uniqColumns: ["slug"]
|
|
38
|
-
},
|
|
39
35
|
SourceReference: {
|
|
40
36
|
prismaModelName: "SourceReference",
|
|
41
|
-
uniqColumns: ["
|
|
37
|
+
uniqColumns: ["resourceId", "url"]
|
|
42
38
|
}
|
|
43
39
|
};
|
|
44
40
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tableSyncMagazine.mjs","names":[],"sources":["../../src/db/tableSyncMagazine.ts"],"sourcesContent":["import type { Prisma } from '../generated/prisma/client'\nimport type { ObjectKeys, ScalarKeys } from './tableSyncPrismaAdapter'\n\ninterface CommonSyncTableInfo<TPrismaModelName extends Prisma.ModelName> {\n prismaModelName: TPrismaModelName\n id?: ScalarKeys<TPrismaModelName>\n uniqColumns: ScalarKeys<TPrismaModelName>[]\n relationColumns?: ObjectKeys<TPrismaModelName>[]\n}\n\ntype TableSyncMagazineType = Partial<{\n [key in Prisma.ModelName]: CommonSyncTableInfo<key>\n}>\n\nexport const TABLE_SYNC_MAGAZINE = {\n DbPerson: {\n id: 'slug',\n prismaModelName: 'DbPerson',\n uniqColumns: ['slug'],\n },\n DbGroup: {\n id: 'slug',\n prismaModelName: 'DbGroup',\n uniqColumns: ['slug'],\n },\n DbGroupMembership: {\n prismaModelName: 'DbGroupMembership',\n uniqColumns: ['groupSlug', 'personSlug'],\n },\n
|
|
1
|
+
{"version":3,"file":"tableSyncMagazine.mjs","names":[],"sources":["../../src/db/tableSyncMagazine.ts"],"sourcesContent":["import type { Prisma } from '../generated/prisma/client'\nimport type { ObjectKeys, ScalarKeys } from './tableSyncPrismaAdapter'\n\ninterface CommonSyncTableInfo<TPrismaModelName extends Prisma.ModelName> {\n prismaModelName: TPrismaModelName\n id?: ScalarKeys<TPrismaModelName>\n uniqColumns: ScalarKeys<TPrismaModelName>[]\n relationColumns?: ObjectKeys<TPrismaModelName>[]\n}\n\ntype TableSyncMagazineType = Partial<{\n [key in Prisma.ModelName]: CommonSyncTableInfo<key>\n}>\n\nexport const TABLE_SYNC_MAGAZINE = {\n DbPerson: {\n id: 'slug',\n prismaModelName: 'DbPerson',\n uniqColumns: ['slug'],\n },\n DbGroup: {\n id: 'slug',\n prismaModelName: 'DbGroup',\n uniqColumns: ['slug'],\n },\n DbGroupMembership: {\n prismaModelName: 'DbGroupMembership',\n uniqColumns: ['groupSlug', 'personSlug'],\n },\n DbResource: {\n prismaModelName: 'DbResource',\n uniqColumns: ['slug'],\n },\n DbAppTagDefinition: {\n prismaModelName: 'DbAppTagDefinition',\n uniqColumns: ['prefix'],\n },\n DbApprovalMethod: {\n id: 'slug',\n prismaModelName: 'DbApprovalMethod',\n uniqColumns: ['slug'],\n },\n Source: {\n id: 'slug',\n prismaModelName: 'Source',\n uniqColumns: ['slug'],\n },\n SourceReference: {\n prismaModelName: 'SourceReference',\n uniqColumns: ['resourceId', 'url'],\n },\n} as const satisfies TableSyncMagazineType\n\nexport type TableSyncMagazine = typeof TABLE_SYNC_MAGAZINE\nexport type TableSyncMagazineModelNameKey = keyof TableSyncMagazine\n"],"mappings":";AAcA,MAAa,sBAAsB;CACjC,UAAU;EACR,IAAI;EACJ,iBAAiB;EACjB,aAAa,CAAC,OAAO;EACtB;CACD,SAAS;EACP,IAAI;EACJ,iBAAiB;EACjB,aAAa,CAAC,OAAO;EACtB;CACD,mBAAmB;EACjB,iBAAiB;EACjB,aAAa,CAAC,aAAa,aAAa;EACzC;CACD,YAAY;EACV,iBAAiB;EACjB,aAAa,CAAC,OAAO;EACtB;CACD,oBAAoB;EAClB,iBAAiB;EACjB,aAAa,CAAC,SAAS;EACxB;CACD,kBAAkB;EAChB,IAAI;EACJ,iBAAiB;EACjB,aAAa,CAAC,OAAO;EACtB;CACD,QAAQ;EACN,IAAI;EACJ,iBAAiB;EACjB,aAAa,CAAC,OAAO;EACtB;CACD,iBAAiB;EACf,iBAAiB;EACjB,aAAa,CAAC,cAAc,MAAM;EACnC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tableSyncPrismaAdapter.mjs","names":[],"sources":["../../src/db/tableSyncPrismaAdapter.ts"],"sourcesContent":["import { tableSync } from '@igstack/app-catalog-table-sync'\nimport type { Prisma, PrismaClient } from '../generated/prisma/client'\nimport { mapValues, omit, pick } from 'radashi'\n\nexport type ScalarKeys<TPrismaModelName extends Prisma.ModelName> =\n keyof Prisma.TypeMap['model'][TPrismaModelName]['payload']['scalars']\nexport type ObjectKeys<TPrismaModelName extends Prisma.ModelName> =\n keyof Prisma.TypeMap['model'][TPrismaModelName]['payload']['objects']\n\nexport type ScalarFilter<TPrismaModelName extends Prisma.ModelName> = Partial<\n Prisma.TypeMap['model'][TPrismaModelName]['payload']['scalars']\n>\n\nexport type GetOperationFns<TModel extends Prisma.ModelName> = {\n [TOperation in keyof Prisma.TypeMap['model']['
|
|
1
|
+
{"version":3,"file":"tableSyncPrismaAdapter.mjs","names":[],"sources":["../../src/db/tableSyncPrismaAdapter.ts"],"sourcesContent":["import { tableSync } from '@igstack/app-catalog-table-sync'\nimport type { Prisma, PrismaClient } from '../generated/prisma/client'\nimport { mapValues, omit, pick } from 'radashi'\n\nexport type ScalarKeys<TPrismaModelName extends Prisma.ModelName> =\n keyof Prisma.TypeMap['model'][TPrismaModelName]['payload']['scalars']\nexport type ObjectKeys<TPrismaModelName extends Prisma.ModelName> =\n keyof Prisma.TypeMap['model'][TPrismaModelName]['payload']['objects']\n\nexport type ScalarFilter<TPrismaModelName extends Prisma.ModelName> = Partial<\n Prisma.TypeMap['model'][TPrismaModelName]['payload']['scalars']\n>\n\nexport type GetOperationFns<TModel extends Prisma.ModelName> = {\n [TOperation in keyof Prisma.TypeMap['model']['DbResource']['operations']]: (\n args: Prisma.TypeMap['model'][TModel]['operations'][TOperation]['args'],\n ) => Promise<\n Prisma.TypeMap['model'][TModel]['operations'][TOperation]['result']\n >\n}\n\nexport interface TableSyncParamsPrisma<\n TPrismaClient extends PrismaClient,\n TPrismaModelName extends Prisma.ModelName,\n TUniqColumns extends readonly ScalarKeys<TPrismaModelName>[],\n TRelationColumns extends readonly ObjectKeys<TPrismaModelName>[],\n> {\n id?: ScalarKeys<TPrismaModelName>\n prisma: TPrismaClient\n prismaModelName: TPrismaModelName\n uniqColumns: TUniqColumns\n relationColumns?: TRelationColumns\n where?: ScalarFilter<TPrismaModelName>\n upsertOnly?: boolean\n}\n\nfunction getPrismaModelOperations<\n TPrismaClient,\n TPrismaModelName extends Prisma.ModelName,\n>(prisma: TPrismaClient, prismaModelName: TPrismaModelName) {\n const key = (prismaModelName.slice(0, 1).toLowerCase() +\n prismaModelName.slice(1)) as keyof TPrismaClient\n return prisma[key] as GetOperationFns<TPrismaModelName>\n}\n\nexport type MakeTFromPrismaModel<TPrismaModelName extends Prisma.ModelName> =\n NonNullable<\n Prisma.TypeMap['model'][TPrismaModelName]['operations']['findUnique']['result']\n >\n\nexport function tableSyncPrisma<\n TPrismaClient extends PrismaClient,\n TPrismaModelName extends Prisma.ModelName,\n TUniqColumns extends readonly ScalarKeys<TPrismaModelName>[],\n TRelationColumns extends readonly ObjectKeys<TPrismaModelName>[],\n TId extends ScalarKeys<TPrismaModelName> = ScalarKeys<TPrismaModelName>,\n>(\n params: TableSyncParamsPrisma<\n TPrismaClient,\n TPrismaModelName,\n TUniqColumns,\n TRelationColumns\n >,\n) {\n const {\n prisma,\n prismaModelName,\n uniqColumns,\n where: whereGlobal,\n upsertOnly,\n } = params\n const prismOperations = getPrismaModelOperations(prisma, prismaModelName)\n\n const idColumn = (params.id ?? 'id') as TId\n // @ts-ignore maybe someday later (never)\n return tableSync<MakeTFromPrismaModel<TPrismaModelName>, TUniqColumns, TId>({\n id: idColumn,\n uniqColumns,\n readAll: async () => {\n const findManyArgs = whereGlobal\n ? {\n where: whereGlobal,\n }\n : {}\n return (await prismOperations.findMany(\n findManyArgs,\n )) as MakeTFromPrismaModel<TPrismaModelName>[]\n },\n writeAll: async (createData, update, deleteIds) => {\n const prismaUniqKey = params.uniqColumns.join('_')\n const relationColumnList =\n params.relationColumns ?? ([] as ObjectKeys<TPrismaModelName>[])\n\n return prisma.$transaction(async (tx) => {\n const txOps = getPrismaModelOperations(tx, prismaModelName)\n for (const { data, where } of update) {\n const uniqKeyWhere =\n Object.keys(where).length > 1\n ? {\n [prismaUniqKey]: where,\n }\n : where\n\n const dataScalar = omit(data, relationColumnList)\n const dataRelations = mapValues(\n pick(data, relationColumnList),\n (value) => {\n return {\n set: value,\n }\n },\n )\n\n // @ts-expect-error This is too difficult for me to come up with right types\n await txOps.update({\n data: { ...dataScalar, ...dataRelations },\n where: { ...uniqKeyWhere },\n })\n }\n\n if (upsertOnly !== true) {\n await txOps.deleteMany({\n where: {\n [idColumn]: {\n in: deleteIds,\n },\n },\n })\n }\n\n // Validate uniqueness of uniqColumns before creating records\n const createDataMapped = createData.map((data) => {\n // @ts-expect-error This is too difficult for me to come up with right types\n const dataScalar = omit(data, relationColumnList)\n\n // @ts-expect-error This is too difficult for me to come up with right types\n const onlyRelationColumns = pick(data, relationColumnList)\n const dataRelations = mapValues(onlyRelationColumns, (value) => {\n return {\n connect: value,\n }\n })\n\n return { ...dataScalar, ...dataRelations }\n })\n\n // Check for duplicates in the data to be created\n if (createDataMapped.length > 0) {\n const uniqKeysInCreate = new Set<string>()\n const duplicateKeys: string[] = []\n\n for (const data of createDataMapped) {\n const keyParts = params.uniqColumns.map((col) => {\n const value = data[col as keyof typeof data]\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive: unique columns may be nullable in schemas\n return value === null || value === undefined\n ? 'null'\n : String(value)\n })\n const key = keyParts.join(':')\n\n if (uniqKeysInCreate.has(key)) {\n duplicateKeys.push(key)\n } else {\n uniqKeysInCreate.add(key)\n }\n }\n\n if (duplicateKeys.length > 0) {\n const uniqColumnsStr = params.uniqColumns.join(', ')\n throw new Error(\n `Duplicate unique key values found in data to be created. ` +\n `Model: ${prismaModelName}, Unique columns: [${uniqColumnsStr}], ` +\n `Duplicate keys: [${duplicateKeys.join(', ')}]`,\n )\n }\n }\n\n const results: MakeTFromPrismaModel<TPrismaModelName>[] = []\n\n if (relationColumnList.length === 0) {\n // @ts-expect-error This is too difficult for me to come up with right types\n const batchResult = await txOps.createManyAndReturn({\n data: createDataMapped,\n })\n\n results.push(...batchResult)\n } else {\n for (const dataMappedElement of createDataMapped) {\n // @ts-expect-error too difficult for me\n const newVar = await txOps.create({\n data: dataMappedElement,\n })\n results.push(newVar as MakeTFromPrismaModel<TPrismaModelName>)\n }\n }\n\n return results\n })\n },\n })\n}\n"],"mappings":";;;;AAoCA,SAAS,yBAGP,QAAuB,iBAAmC;AAG1D,QAAO,OAFM,gBAAgB,MAAM,GAAG,EAAE,CAAC,aAAa,GACpD,gBAAgB,MAAM,EAAE;;AAS5B,SAAgB,gBAOd,QAMA;CACA,MAAM,EACJ,QACA,iBACA,aACA,OAAO,aACP,eACE;CACJ,MAAM,kBAAkB,yBAAyB,QAAQ,gBAAgB;CAEzE,MAAM,WAAY,OAAO,MAAM;AAE/B,QAAO,UAAqE;EAC1E,IAAI;EACJ;EACA,SAAS,YAAY;GACnB,MAAM,eAAe,cACjB,EACE,OAAO,aACR,GACD,EAAE;AACN,UAAQ,MAAM,gBAAgB,SAC5B,aACD;;EAEH,UAAU,OAAO,YAAY,QAAQ,cAAc;GACjD,MAAM,gBAAgB,OAAO,YAAY,KAAK,IAAI;GAClD,MAAM,qBACJ,OAAO,mBAAoB,EAAE;AAE/B,UAAO,OAAO,aAAa,OAAO,OAAO;IACvC,MAAM,QAAQ,yBAAyB,IAAI,gBAAgB;AAC3D,SAAK,MAAM,EAAE,MAAM,WAAW,QAAQ;KACpC,MAAM,eACJ,OAAO,KAAK,MAAM,CAAC,SAAS,IACxB,GACG,gBAAgB,OAClB,GACD;KAEN,MAAM,aAAa,KAAK,MAAM,mBAAmB;KACjD,MAAM,gBAAgB,UACpB,KAAK,MAAM,mBAAmB,GAC7B,UAAU;AACT,aAAO,EACL,KAAK,OACN;OAEJ;AAGD,WAAM,MAAM,OAAO;MACjB,MAAM;OAAE,GAAG;OAAY,GAAG;OAAe;MACzC,OAAO,EAAE,GAAG,cAAc;MAC3B,CAAC;;AAGJ,QAAI,eAAe,KACjB,OAAM,MAAM,WAAW,EACrB,OAAO,GACJ,WAAW,EACV,IAAI,WACL,EACF,EACF,CAAC;IAIJ,MAAM,mBAAmB,WAAW,KAAK,SAAS;KAEhD,MAAM,aAAa,KAAK,MAAM,mBAAmB;KAIjD,MAAM,gBAAgB,UADM,KAAK,MAAM,mBAAmB,GACJ,UAAU;AAC9D,aAAO,EACL,SAAS,OACV;OACD;AAEF,YAAO;MAAE,GAAG;MAAY,GAAG;MAAe;MAC1C;AAGF,QAAI,iBAAiB,SAAS,GAAG;KAC/B,MAAM,mCAAmB,IAAI,KAAa;KAC1C,MAAM,gBAA0B,EAAE;AAElC,UAAK,MAAM,QAAQ,kBAAkB;MAQnC,MAAM,MAPW,OAAO,YAAY,KAAK,QAAQ;OAC/C,MAAM,QAAQ,KAAK;AAEnB,cAAO,UAAU,QAAQ,UAAU,SAC/B,SACA,OAAO,MAAM;QACjB,CACmB,KAAK,IAAI;AAE9B,UAAI,iBAAiB,IAAI,IAAI,CAC3B,eAAc,KAAK,IAAI;UAEvB,kBAAiB,IAAI,IAAI;;AAI7B,SAAI,cAAc,SAAS,GAAG;MAC5B,MAAM,iBAAiB,OAAO,YAAY,KAAK,KAAK;AACpD,YAAM,IAAI,MACR,mEACY,gBAAgB,qBAAqB,eAAe,sBAC1C,cAAc,KAAK,KAAK,CAAC,GAChD;;;IAIL,MAAM,UAAoD,EAAE;AAE5D,QAAI,mBAAmB,WAAW,GAAG;KAEnC,MAAM,cAAc,MAAM,MAAM,oBAAoB,EAClD,MAAM,kBACP,CAAC;AAEF,aAAQ,KAAK,GAAG,YAAY;UAE5B,MAAK,MAAM,qBAAqB,kBAAkB;KAEhD,MAAM,SAAS,MAAM,MAAM,OAAO,EAChC,MAAM,mBACP,CAAC;AACF,aAAQ,KAAK,OAAiD;;AAIlE,WAAO;KACP;;EAEL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":["$Class.getPrismaClientClass"],"sources":["../../../src/generated/prisma/client.ts"],"sourcesContent":["\n/* !!! This is code generated by Prisma. Do not edit directly. !!! */\n/* eslint-disable */\n// biome-ignore-all lint: generated file\n// @ts-nocheck \n/*\n * This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.\n * If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.\n *\n * 🟢 You can import this file directly.\n */\n\nimport * as process from 'node:process'\nimport * as path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nglobalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))\n\nimport * as runtime from \"@prisma/client/runtime/client\"\nimport * as $Enums from \"./enums\"\nimport * as $Class from \"./internal/class\"\nimport * as Prisma from \"./internal/prismaNamespace\"\n\nexport * as $Enums from './enums'\nexport * from \"./enums\"\n/**\n * ## Prisma Client\n * \n * Type-safe database client for TypeScript\n * @example\n * ```\n * const prisma = new PrismaClient({\n * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })\n * })\n * // Fetch zero or more Users\n * const users = await prisma.user.findMany()\n * ```\n * \n * Read more in our [docs](https://pris.ly/d/client).\n */\nexport const PrismaClient = $Class.getPrismaClientClass()\nexport type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions[\"omit\"] = Prisma.PrismaClientOptions[\"omit\"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>\nexport { Prisma }\n\n/**\n * Model user\n * \n */\nexport type user = Prisma.userModel\n/**\n * Model session\n * \n */\nexport type session = Prisma.sessionModel\n/**\n * Model account\n * \n */\nexport type account = Prisma.accountModel\n/**\n * Model verification\n * \n */\nexport type verification = Prisma.verificationModel\n/**\n * Model DbPerson\n * \n */\nexport type DbPerson = Prisma.DbPersonModel\n/**\n * Model DbGroup\n * \n */\nexport type DbGroup = Prisma.DbGroupModel\n/**\n * Model DbGroupMembership\n * \n */\nexport type DbGroupMembership = Prisma.DbGroupMembershipModel\n/**\n * Model DbApprovalMethod\n * \n */\nexport type DbApprovalMethod = Prisma.DbApprovalMethodModel\n/**\n * Model
|
|
1
|
+
{"version":3,"file":"client.mjs","names":["$Class.getPrismaClientClass"],"sources":["../../../src/generated/prisma/client.ts"],"sourcesContent":["\n/* !!! This is code generated by Prisma. Do not edit directly. !!! */\n/* eslint-disable */\n// biome-ignore-all lint: generated file\n// @ts-nocheck \n/*\n * This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.\n * If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.\n *\n * 🟢 You can import this file directly.\n */\n\nimport * as process from 'node:process'\nimport * as path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nglobalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))\n\nimport * as runtime from \"@prisma/client/runtime/client\"\nimport * as $Enums from \"./enums\"\nimport * as $Class from \"./internal/class\"\nimport * as Prisma from \"./internal/prismaNamespace\"\n\nexport * as $Enums from './enums'\nexport * from \"./enums\"\n/**\n * ## Prisma Client\n * \n * Type-safe database client for TypeScript\n * @example\n * ```\n * const prisma = new PrismaClient({\n * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })\n * })\n * // Fetch zero or more Users\n * const users = await prisma.user.findMany()\n * ```\n * \n * Read more in our [docs](https://pris.ly/d/client).\n */\nexport const PrismaClient = $Class.getPrismaClientClass()\nexport type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions[\"omit\"] = Prisma.PrismaClientOptions[\"omit\"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>\nexport { Prisma }\n\n/**\n * Model user\n * \n */\nexport type user = Prisma.userModel\n/**\n * Model session\n * \n */\nexport type session = Prisma.sessionModel\n/**\n * Model account\n * \n */\nexport type account = Prisma.accountModel\n/**\n * Model verification\n * \n */\nexport type verification = Prisma.verificationModel\n/**\n * Model DbPerson\n * \n */\nexport type DbPerson = Prisma.DbPersonModel\n/**\n * Model DbGroup\n * \n */\nexport type DbGroup = Prisma.DbGroupModel\n/**\n * Model DbGroupMembership\n * \n */\nexport type DbGroupMembership = Prisma.DbGroupMembershipModel\n/**\n * Model DbApprovalMethod\n * \n */\nexport type DbApprovalMethod = Prisma.DbApprovalMethodModel\n/**\n * Model DbResource\n * \n */\nexport type DbResource = Prisma.DbResourceModel\n/**\n * Model DbAppTagDefinition\n * \n */\nexport type DbAppTagDefinition = Prisma.DbAppTagDefinitionModel\n/**\n * Model DbAsset\n * \n */\nexport type DbAsset = Prisma.DbAssetModel\n/**\n * Model Source\n * \n */\nexport type Source = Prisma.SourceModel\n/**\n * Model SourceReference\n * \n */\nexport type SourceReference = Prisma.SourceReferenceModel\n"],"mappings":";;;;;AAeA,WAAW,eAAe,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;AAwBtE,MAAa,eAAeA,sBAA6B"}
|
|
@@ -6,8 +6,7 @@ import { DbPersonDelegate } from "../models/DbPerson.mjs";
|
|
|
6
6
|
import { DbGroupDelegate } from "../models/DbGroup.mjs";
|
|
7
7
|
import { DbGroupMembershipDelegate } from "../models/DbGroupMembership.mjs";
|
|
8
8
|
import { DbApprovalMethodDelegate } from "../models/DbApprovalMethod.mjs";
|
|
9
|
-
import {
|
|
10
|
-
import { DbSubResourceDelegate } from "../models/DbSubResource.mjs";
|
|
9
|
+
import { DbResourceDelegate } from "../models/DbResource.mjs";
|
|
11
10
|
import { DbAppTagDefinitionDelegate } from "../models/DbAppTagDefinition.mjs";
|
|
12
11
|
import { DbAssetDelegate } from "../models/DbAsset.mjs";
|
|
13
12
|
import { SourceDelegate } from "../models/Source.mjs";
|
|
@@ -220,25 +219,14 @@ interface PrismaClient<in LogOpts extends LogLevel = never, in out OmitOpts exte
|
|
|
220
219
|
omit: OmitOpts;
|
|
221
220
|
}>;
|
|
222
221
|
/**
|
|
223
|
-
* `prisma.
|
|
222
|
+
* `prisma.dbResource`: Exposes CRUD operations for the **DbResource** model.
|
|
224
223
|
* Example usage:
|
|
225
224
|
* ```ts
|
|
226
|
-
* // Fetch zero or more
|
|
227
|
-
* const
|
|
225
|
+
* // Fetch zero or more DbResources
|
|
226
|
+
* const dbResources = await prisma.dbResource.findMany()
|
|
228
227
|
* ```
|
|
229
228
|
*/
|
|
230
|
-
get
|
|
231
|
-
omit: OmitOpts;
|
|
232
|
-
}>;
|
|
233
|
-
/**
|
|
234
|
-
* `prisma.dbSubResource`: Exposes CRUD operations for the **DbSubResource** model.
|
|
235
|
-
* Example usage:
|
|
236
|
-
* ```ts
|
|
237
|
-
* // Fetch zero or more DbSubResources
|
|
238
|
-
* const dbSubResources = await prisma.dbSubResource.findMany()
|
|
239
|
-
* ```
|
|
240
|
-
*/
|
|
241
|
-
get dbSubResource(): DbSubResourceDelegate<ExtArgs, {
|
|
229
|
+
get dbResource(): DbResourceDelegate<ExtArgs, {
|
|
242
230
|
omit: OmitOpts;
|
|
243
231
|
}>;
|
|
244
232
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class.d.mts","names":[],"sources":["../../../../src/generated/prisma/internal/class.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"class.d.mts","names":[],"sources":["../../../../src/generated/prisma/internal/class.ts"],"mappings":"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"}
|