@igstack/app-catalog-backend-core 0.3.1-alpha-20260305181002 → 0.3.1-alpha-20260306003829

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/db/client.d.mts +26 -0
  2. package/dist/db/client.d.mts.map +1 -0
  3. package/dist/db/client.mjs +52 -0
  4. package/dist/db/client.mjs.map +1 -0
  5. package/dist/db/index.d.mts +4 -0
  6. package/dist/db/syncAppCatalog.d.mts +20 -0
  7. package/dist/db/syncAppCatalog.d.mts.map +1 -0
  8. package/dist/db/syncAppCatalog.mjs +126 -0
  9. package/dist/db/syncAppCatalog.mjs.map +1 -0
  10. package/dist/db/tableSyncMagazine.d.mts +21 -0
  11. package/dist/db/tableSyncMagazine.d.mts.map +1 -0
  12. package/dist/db/tableSyncMagazine.mjs +20 -0
  13. package/dist/db/tableSyncMagazine.mjs.map +1 -0
  14. package/dist/db/tableSyncPrismaAdapter.d.mts +27 -0
  15. package/dist/db/tableSyncPrismaAdapter.d.mts.map +1 -0
  16. package/dist/db/tableSyncPrismaAdapter.mjs +81 -0
  17. package/dist/db/tableSyncPrismaAdapter.mjs.map +1 -0
  18. package/dist/generated/prisma/client.d.mts +26 -0
  19. package/dist/generated/prisma/client.d.mts.map +1 -0
  20. package/dist/generated/prisma/client.mjs +26 -0
  21. package/dist/generated/prisma/client.mjs.map +1 -0
  22. package/dist/generated/prisma/commonInputTypes.d.mts +564 -0
  23. package/dist/generated/prisma/commonInputTypes.d.mts.map +1 -0
  24. package/dist/generated/prisma/enums.d.mts +15 -0
  25. package/dist/generated/prisma/enums.d.mts.map +1 -0
  26. package/dist/generated/prisma/internal/class.d.mts +219 -0
  27. package/dist/generated/prisma/internal/class.d.mts.map +1 -0
  28. package/dist/generated/prisma/internal/class.mjs +44 -0
  29. package/dist/generated/prisma/internal/class.mjs.map +1 -0
  30. package/dist/generated/prisma/internal/prismaNamespace.d.mts +1102 -0
  31. package/dist/generated/prisma/internal/prismaNamespace.d.mts.map +1 -0
  32. package/dist/generated/prisma/models/DbAppForCatalog.d.mts +1279 -0
  33. package/dist/generated/prisma/models/DbAppForCatalog.d.mts.map +1 -0
  34. package/dist/generated/prisma/models/DbAppTagDefinition.d.mts +1007 -0
  35. package/dist/generated/prisma/models/DbAppTagDefinition.d.mts.map +1 -0
  36. package/dist/generated/prisma/models/DbApprovalMethod.d.mts +989 -0
  37. package/dist/generated/prisma/models/DbApprovalMethod.d.mts.map +1 -0
  38. package/dist/generated/prisma/models/DbAsset.d.mts +1189 -0
  39. package/dist/generated/prisma/models/DbAsset.d.mts.map +1 -0
  40. package/dist/generated/prisma/models/account.d.mts +1410 -0
  41. package/dist/generated/prisma/models/account.d.mts.map +1 -0
  42. package/dist/generated/prisma/models/session.d.mts +1232 -0
  43. package/dist/generated/prisma/models/session.d.mts.map +1 -0
  44. package/dist/generated/prisma/models/user.d.mts +1343 -0
  45. package/dist/generated/prisma/models/user.d.mts.map +1 -0
  46. package/dist/generated/prisma/models/verification.d.mts +988 -0
  47. package/dist/generated/prisma/models/verification.d.mts.map +1 -0
  48. package/dist/generated/prisma/models.d.mts +9 -0
  49. package/dist/index.d.mts +34 -0
  50. package/dist/index.mjs +22 -0
  51. package/dist/middleware/backendResolver.mjs +26 -0
  52. package/dist/middleware/backendResolver.mjs.map +1 -0
  53. package/dist/middleware/createEhMiddleware.d.mts +7 -0
  54. package/dist/middleware/createEhMiddleware.d.mts.map +1 -0
  55. package/dist/middleware/createEhMiddleware.mjs +107 -0
  56. package/dist/middleware/createEhMiddleware.mjs.map +1 -0
  57. package/dist/middleware/database.d.mts +24 -0
  58. package/dist/middleware/database.d.mts.map +1 -0
  59. package/dist/middleware/database.mjs +58 -0
  60. package/dist/middleware/database.mjs.map +1 -0
  61. package/dist/middleware/featureRegistry.mjs +71 -0
  62. package/dist/middleware/featureRegistry.mjs.map +1 -0
  63. package/dist/middleware/htmlInjection.d.mts +15 -0
  64. package/dist/middleware/htmlInjection.d.mts.map +1 -0
  65. package/dist/middleware/htmlInjection.mjs +20 -0
  66. package/dist/middleware/htmlInjection.mjs.map +1 -0
  67. package/dist/middleware/index.d.mts +4 -0
  68. package/dist/middleware/types.d.mts +198 -0
  69. package/dist/middleware/types.d.mts.map +1 -0
  70. package/dist/modules/appCatalog/checkLinks.d.mts +37 -0
  71. package/dist/modules/appCatalog/checkLinks.d.mts.map +1 -0
  72. package/dist/modules/appCatalog/checkLinks.mjs +194 -0
  73. package/dist/modules/appCatalog/checkLinks.mjs.map +1 -0
  74. package/dist/modules/appCatalog/service.mjs +102 -0
  75. package/dist/modules/appCatalog/service.mjs.map +1 -0
  76. package/dist/modules/assets/assetRestController.d.mts +22 -0
  77. package/dist/modules/assets/assetRestController.d.mts.map +1 -0
  78. package/dist/modules/assets/assetRestController.mjs +176 -0
  79. package/dist/modules/assets/assetRestController.mjs.map +1 -0
  80. package/dist/modules/assets/assetUtils.mjs +87 -0
  81. package/dist/modules/assets/assetUtils.mjs.map +1 -0
  82. package/dist/modules/assets/screenshotRestController.d.mts +21 -0
  83. package/dist/modules/assets/screenshotRestController.d.mts.map +1 -0
  84. package/dist/modules/assets/screenshotRestController.mjs +147 -0
  85. package/dist/modules/assets/screenshotRestController.mjs.map +1 -0
  86. package/dist/modules/assets/syncAssets.d.mts +28 -0
  87. package/dist/modules/assets/syncAssets.d.mts.map +1 -0
  88. package/dist/modules/assets/syncAssets.mjs +178 -0
  89. package/dist/modules/assets/syncAssets.mjs.map +1 -0
  90. package/dist/modules/assets/upsertAsset.mjs +25 -0
  91. package/dist/modules/assets/upsertAsset.mjs.map +1 -0
  92. package/dist/modules/auth/auth.d.mts +42 -0
  93. package/dist/modules/auth/auth.d.mts.map +1 -0
  94. package/dist/modules/auth/auth.mjs +32 -0
  95. package/dist/modules/auth/auth.mjs.map +1 -0
  96. package/dist/modules/auth/authRouter.d.mts +46 -0
  97. package/dist/modules/auth/authRouter.d.mts.map +1 -0
  98. package/dist/modules/auth/authRouter.mjs +40 -0
  99. package/dist/modules/auth/authRouter.mjs.map +1 -0
  100. package/dist/modules/auth/authorizationUtils.d.mts +56 -0
  101. package/dist/modules/auth/authorizationUtils.d.mts.map +1 -0
  102. package/dist/modules/auth/authorizationUtils.mjs +64 -0
  103. package/dist/modules/auth/authorizationUtils.mjs.map +1 -0
  104. package/dist/modules/auth/devMockUserUtils.mjs +43 -0
  105. package/dist/modules/auth/devMockUserUtils.mjs.map +1 -0
  106. package/dist/modules/auth/registerAuthRoutes.d.mts +13 -0
  107. package/dist/modules/auth/registerAuthRoutes.d.mts.map +1 -0
  108. package/dist/modules/auth/registerAuthRoutes.mjs +26 -0
  109. package/dist/modules/auth/registerAuthRoutes.mjs.map +1 -0
  110. package/dist/modules/icons/iconRestController.d.mts +21 -0
  111. package/dist/modules/icons/iconRestController.d.mts.map +1 -0
  112. package/dist/modules/icons/iconRestController.mjs +121 -0
  113. package/dist/modules/icons/iconRestController.mjs.map +1 -0
  114. package/dist/modules/icons/iconService.d.mts +57 -0
  115. package/dist/modules/icons/iconService.d.mts.map +1 -0
  116. package/dist/modules/icons/iconService.mjs +65 -0
  117. package/dist/modules/icons/iconService.mjs.map +1 -0
  118. package/dist/modules/icons/iconUtils.mjs +28 -0
  119. package/dist/modules/icons/iconUtils.mjs.map +1 -0
  120. package/dist/server/controller.d.mts +63 -0
  121. package/dist/server/controller.d.mts.map +1 -0
  122. package/dist/server/controller.mjs +29 -0
  123. package/dist/server/controller.mjs.map +1 -0
  124. package/dist/server/ehStaticControllerContract.d.mts +17 -0
  125. package/dist/server/ehStaticControllerContract.d.mts.map +1 -0
  126. package/dist/server/ehStaticControllerContract.mjs +15 -0
  127. package/dist/server/ehStaticControllerContract.mjs.map +1 -0
  128. package/dist/server/ehTrpcContext.d.mts +22 -0
  129. package/dist/server/ehTrpcContext.d.mts.map +1 -0
  130. package/dist/server/ehTrpcContext.mjs +12 -0
  131. package/dist/server/ehTrpcContext.mjs.map +1 -0
  132. package/dist/server/trpcSetup.mjs +71 -0
  133. package/dist/server/trpcSetup.mjs.map +1 -0
  134. package/dist/types/backend/api.d.mts +60 -0
  135. package/dist/types/backend/api.d.mts.map +1 -0
  136. package/dist/types/backend/common.d.mts +13 -0
  137. package/dist/types/backend/common.d.mts.map +1 -0
  138. package/dist/types/backend/companySpecificBackend.d.mts +14 -0
  139. package/dist/types/backend/companySpecificBackend.d.mts.map +1 -0
  140. package/dist/types/backend/dataSources.d.mts +25 -0
  141. package/dist/types/backend/dataSources.d.mts.map +1 -0
  142. package/dist/types/backend/deployments.d.mts +39 -0
  143. package/dist/types/backend/deployments.d.mts.map +1 -0
  144. package/dist/types/common/app/appTypes.d.mts +17 -0
  145. package/dist/types/common/app/appTypes.d.mts.map +1 -0
  146. package/dist/types/common/app/ui/appUiTypes.d.mts +15 -0
  147. package/dist/types/common/app/ui/appUiTypes.d.mts.map +1 -0
  148. package/dist/types/common/appCatalogTypes.d.mts +58 -0
  149. package/dist/types/common/appCatalogTypes.d.mts.map +1 -0
  150. package/dist/types/common/approvalMethodTypes.d.mts +104 -0
  151. package/dist/types/common/approvalMethodTypes.d.mts.map +1 -0
  152. package/dist/types/common/env/envTypes.d.mts +11 -0
  153. package/dist/types/common/env/envTypes.d.mts.map +1 -0
  154. package/dist/types/common/resourceTypes.d.mts +12 -0
  155. package/dist/types/common/resourceTypes.d.mts.map +1 -0
  156. package/dist/types/common/sharedTypes.d.mts +8 -0
  157. package/dist/types/common/sharedTypes.d.mts.map +1 -0
  158. package/package.json +14 -17
  159. package/src/db/client.ts +2 -1
  160. package/dist/index.d.ts +0 -12304
  161. package/dist/index.d.ts.map +0 -1
  162. package/dist/index.js +0 -6250
  163. package/dist/index.js.map +0 -1
@@ -0,0 +1,26 @@
1
+ import { PrismaClient } from "../generated/prisma/client.mjs";
2
+
3
+ //#region src/db/client.d.ts
4
+ /**
5
+ * Gets the internal Prisma client instance.
6
+ * Creates one if it doesn't exist.
7
+ */
8
+ declare function getDbClient(): PrismaClient;
9
+ /**
10
+ * Sets the internal Prisma client instance.
11
+ * Used by middleware to bridge with existing getDbClient() usage.
12
+ */
13
+ declare function setDbClient(client: PrismaClient): void;
14
+ /**
15
+ * Connects to the database.
16
+ * Call this before performing database operations.
17
+ */
18
+ declare function connectDb(): Promise<void>;
19
+ /**
20
+ * Disconnects from the database.
21
+ * Call this when done with database operations (e.g., in scripts).
22
+ */
23
+ declare function disconnectDb(): Promise<void>;
24
+ //#endregion
25
+ export { connectDb, disconnectDb, getDbClient, setDbClient };
26
+ //# sourceMappingURL=client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../../src/db/client.ts"],"mappings":";;;;;AAWA;;iBAAgB,WAAA,CAAA,GAAe,YAAA;;;AAuB/B;;iBAAgB,WAAA,CAAY,MAAA,EAAQ,YAAA;;;AAQpC;;iBAAsB,SAAA,CAAA,GAAa,OAAA;;;AASnC;;iBAAsB,YAAA,CAAA,GAAgB,OAAA"}
@@ -0,0 +1,52 @@
1
+ import { PrismaClient } from "../generated/prisma/client.mjs";
2
+ import { PrismaPg } from "@prisma/adapter-pg";
3
+ import pg from "pg";
4
+
5
+ //#region src/db/client.ts
6
+ let prismaClient = null;
7
+ let pool = null;
8
+ /**
9
+ * Gets the internal Prisma client instance.
10
+ * Creates one if it doesn't exist.
11
+ */
12
+ function getDbClient() {
13
+ if (!prismaClient) {
14
+ const databaseUrl = process.env.AC_CORE_DATABASE_URL;
15
+ if (!databaseUrl) throw new Error("PrismaClient not initialized. You must call createEhMiddleware() before using database functions, or set AC_CORE_DATABASE_URL environment variable for standalone usage.");
16
+ pool = new pg.Pool({ connectionString: databaseUrl });
17
+ prismaClient = new PrismaClient({ adapter: new PrismaPg(pool) });
18
+ }
19
+ return prismaClient;
20
+ }
21
+ /**
22
+ * Sets the internal Prisma client instance.
23
+ * Used by middleware to bridge with existing getDbClient() usage.
24
+ */
25
+ function setDbClient(client) {
26
+ prismaClient = client;
27
+ }
28
+ /**
29
+ * Connects to the database.
30
+ * Call this before performing database operations.
31
+ */
32
+ async function connectDb() {
33
+ await getDbClient().$connect();
34
+ }
35
+ /**
36
+ * Disconnects from the database.
37
+ * Call this when done with database operations (e.g., in scripts).
38
+ */
39
+ async function disconnectDb() {
40
+ if (prismaClient) {
41
+ await prismaClient.$disconnect();
42
+ prismaClient = null;
43
+ }
44
+ if (pool) {
45
+ await pool.end();
46
+ pool = null;
47
+ }
48
+ }
49
+
50
+ //#endregion
51
+ export { connectDb, disconnectDb, getDbClient, setDbClient };
52
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../../src/db/client.ts"],"sourcesContent":["import { PrismaClient } from '../generated/prisma/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\nimport pg from 'pg'\n\nlet prismaClient: PrismaClient | null = null\nlet pool: pg.Pool | null = null\n\n/**\n * Gets the internal Prisma client instance.\n * Creates one if it doesn't exist.\n */\nexport function getDbClient(): PrismaClient {\n if (!prismaClient) {\n const databaseUrl = process.env.AC_CORE_DATABASE_URL\n if (!databaseUrl) {\n throw new Error(\n 'PrismaClient not initialized. You must call createEhMiddleware() before using database functions, ' +\n 'or set AC_CORE_DATABASE_URL environment variable for standalone usage.',\n )\n }\n\n // Prisma 7 with adapter: Create pg pool and wrap with adapter\n pool = new pg.Pool({ connectionString: databaseUrl })\n const adapter = new PrismaPg(pool)\n\n prismaClient = new PrismaClient({ adapter })\n }\n return prismaClient\n}\n\n/**\n * Sets the internal Prisma client instance.\n * Used by middleware to bridge with existing getDbClient() usage.\n */\nexport function setDbClient(client: PrismaClient): void {\n prismaClient = client\n}\n\n/**\n * Connects to the database.\n * Call this before performing database operations.\n */\nexport async function connectDb(): Promise<void> {\n const client = getDbClient()\n await client.$connect()\n}\n\n/**\n * Disconnects from the database.\n * Call this when done with database operations (e.g., in scripts).\n */\nexport async function disconnectDb(): Promise<void> {\n if (prismaClient) {\n await prismaClient.$disconnect()\n prismaClient = null\n }\n if (pool) {\n await pool.end()\n pool = null\n }\n}\n"],"mappings":";;;;;AAIA,IAAI,eAAoC;AACxC,IAAI,OAAuB;;;;;AAM3B,SAAgB,cAA4B;AAC1C,KAAI,CAAC,cAAc;EACjB,MAAM,cAAc,QAAQ,IAAI;AAChC,MAAI,CAAC,YACH,OAAM,IAAI,MACR,2KAED;AAIH,SAAO,IAAI,GAAG,KAAK,EAAE,kBAAkB,aAAa,CAAC;AAGrD,iBAAe,IAAI,aAAa,EAAE,SAFlB,IAAI,SAAS,KAAK,EAES,CAAC;;AAE9C,QAAO;;;;;;AAOT,SAAgB,YAAY,QAA4B;AACtD,gBAAe;;;;;;AAOjB,eAAsB,YAA2B;AAE/C,OADe,aAAa,CACf,UAAU;;;;;;AAOzB,eAAsB,eAA8B;AAClD,KAAI,cAAc;AAChB,QAAM,aAAa,aAAa;AAChC,iBAAe;;AAEjB,KAAI,MAAM;AACR,QAAM,KAAK,KAAK;AAChB,SAAO"}
@@ -0,0 +1,4 @@
1
+ import { connectDb, disconnectDb, getDbClient, setDbClient } from "./client.mjs";
2
+ import { MakeTFromPrismaModel, ObjectKeys, ScalarFilter, ScalarKeys, TableSyncParamsPrisma, tableSyncPrisma } from "./tableSyncPrismaAdapter.mjs";
3
+ import { TABLE_SYNC_MAGAZINE, TableSyncMagazine, TableSyncMagazineModelNameKey } from "./tableSyncMagazine.mjs";
4
+ import { SyncAppCatalogResult, syncAppCatalog } from "./syncAppCatalog.mjs";
@@ -0,0 +1,20 @@
1
+ import { ApprovalMethod } from "../types/common/approvalMethodTypes.mjs";
2
+ import { AppForCatalog, GroupingTagDefinition } from "../types/common/appCatalogTypes.mjs";
3
+
4
+ //#region src/db/syncAppCatalog.d.ts
5
+ interface SyncAppCatalogResult {
6
+ created: number;
7
+ updated: number;
8
+ deleted: number;
9
+ total: number;
10
+ }
11
+ /**
12
+ * Syncs app catalog data to the database using table sync.
13
+ * This will create new apps, update existing ones, and delete any that are no longer in the input.
14
+ *
15
+ * Note: Call connectDb() before and disconnectDb() after if running in a script.
16
+ */
17
+ declare function syncAppCatalog(apps: Array<AppForCatalog>, tagsDefinitions: Array<GroupingTagDefinition>, approvalMethods: Array<ApprovalMethod>, sreenshotsPath?: string): Promise<SyncAppCatalogResult>;
18
+ //#endregion
19
+ export { SyncAppCatalogResult, syncAppCatalog };
20
+ //# sourceMappingURL=syncAppCatalog.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncAppCatalog.d.mts","names":[],"sources":["../../src/db/syncAppCatalog.ts"],"mappings":";;;;UAaiB,oBAAA;EACf,OAAA;EACA,OAAA;EACA,OAAA;EACA,KAAA;AAAA;;;;;;;iBA4NoB,cAAA,CACpB,IAAA,EAAM,KAAA,CAAM,aAAA,GACZ,eAAA,EAAiB,KAAA,CAAM,qBAAA,GACvB,eAAA,EAAiB,KAAA,CAAM,cAAA,GACvB,cAAA,YACC,OAAA,CAAQ,oBAAA"}
@@ -0,0 +1,126 @@
1
+ import { getDbClient } from "./client.mjs";
2
+ import { tableSyncPrisma } from "./tableSyncPrismaAdapter.mjs";
3
+ import { TABLE_SYNC_MAGAZINE } from "./tableSyncMagazine.mjs";
4
+ import { upsertAsset } from "../modules/assets/upsertAsset.mjs";
5
+ import { group } from "radashi";
6
+ import { readFile, readdir, stat } from "node:fs/promises";
7
+
8
+ //#region src/db/syncAppCatalog.ts
9
+ function isFileNotFoundError(error) {
10
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
11
+ }
12
+ async function processAssetDirectory(dirPath, appSlug, assetType, prisma) {
13
+ try {
14
+ const files = await readdir(dirPath);
15
+ const assetIds = [];
16
+ for (let i = 0; i < files.length; i++) {
17
+ const fileName = files[i];
18
+ if (!fileName) continue;
19
+ const assetName = assetType === "screenshot" ? `${appSlug}-screenshot-${i + 1}` : `${appSlug}-icon`;
20
+ const id = await upsertAsset({
21
+ prisma,
22
+ buffer: await readFile(`${dirPath}/${fileName}`),
23
+ originalFilename: fileName,
24
+ name: assetName,
25
+ assetType
26
+ });
27
+ assetIds.push(id);
28
+ if (assetType === "icon") break;
29
+ }
30
+ return assetIds;
31
+ } catch (error) {
32
+ if (isFileNotFoundError(error)) return [];
33
+ throw error;
34
+ }
35
+ }
36
+ async function syncAppAssets(appSlug, appPath, prisma) {
37
+ return {
38
+ screenshotIds: await processAssetDirectory(`${appPath}/screenshots`, appSlug, "screenshot", prisma),
39
+ iconName: (await processAssetDirectory(`${appPath}/icons`, appSlug, "icon", prisma)).length > 0 ? `${appSlug}-icon` : null
40
+ };
41
+ }
42
+ async function syncAssetsFromFileSystem(apps, allAppsAssetsPath) {
43
+ const appDirectories = await readdir(allAppsAssetsPath);
44
+ const prisma = getDbClient();
45
+ const bySlug = group(apps, (a) => a.slug);
46
+ for (const appDirName of appDirectories) {
47
+ try {
48
+ if (!(await stat(`${allAppsAssetsPath}/${appDirName}`)).isDirectory()) continue;
49
+ } catch (error) {
50
+ if (isFileNotFoundError(error)) continue;
51
+ throw error;
52
+ }
53
+ const appSlug = appDirName;
54
+ if (!bySlug[appSlug]) throw new Error(`App '${appSlug}' does not exist in the app catalog. Existing apps: ${Object.keys(bySlug).join(", ")}`);
55
+ try {
56
+ const { screenshotIds, iconName } = await syncAppAssets(appSlug, `${allAppsAssetsPath}/${appDirName}`, prisma);
57
+ const updateData = {};
58
+ if (screenshotIds.length > 0) updateData.screenshotIds = screenshotIds;
59
+ if (iconName !== null) updateData.iconName = iconName;
60
+ if (Object.keys(updateData).length > 0) await prisma.dbAppForCatalog.update({
61
+ where: { slug: appSlug },
62
+ data: updateData
63
+ });
64
+ } catch (error) {
65
+ const errorMessage = error instanceof Error ? error.message : String(error);
66
+ throw new Error(`Error while upserting assets for app '${appSlug}': ${errorMessage}`);
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * Syncs app catalog data to the database using table sync.
72
+ * This will create new apps, update existing ones, and delete any that are no longer in the input.
73
+ *
74
+ * Note: Call connectDb() before and disconnectDb() after if running in a script.
75
+ */
76
+ async function syncAppCatalog(apps, tagsDefinitions, approvalMethods, sreenshotsPath) {
77
+ try {
78
+ const prisma = getDbClient();
79
+ await tableSyncPrisma({
80
+ prisma,
81
+ ...TABLE_SYNC_MAGAZINE.DbApprovalMethod
82
+ }).sync(approvalMethods);
83
+ const sync = tableSyncPrisma({
84
+ prisma,
85
+ ...TABLE_SYNC_MAGAZINE.DbAppForCatalog
86
+ });
87
+ await tableSyncPrisma({
88
+ prisma,
89
+ ...TABLE_SYNC_MAGAZINE.DbAppTagDefinition
90
+ }).sync(tagsDefinitions);
91
+ const dbApps = apps.map((app) => {
92
+ return {
93
+ slug: app.slug || app.displayName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""),
94
+ displayName: app.displayName,
95
+ description: app.description,
96
+ teams: app.teams ?? [],
97
+ accessRequest: app.accessRequest ?? null,
98
+ notes: app.notes ?? null,
99
+ tags: app.tags ?? [],
100
+ appUrl: app.appUrl ?? null,
101
+ links: app.links ?? null,
102
+ iconName: app.iconName ?? null,
103
+ screenshotIds: app.screenshotIds ?? [],
104
+ sources: app.sources ?? [],
105
+ deprecated: app.deprecated ?? null
106
+ };
107
+ });
108
+ const actual = (await sync.sync(dbApps)).getActual();
109
+ if (sreenshotsPath) await syncAssetsFromFileSystem(apps, sreenshotsPath);
110
+ else console.warn("Do not sync screenhots");
111
+ return {
112
+ created: actual.length - apps.length + (apps.length - actual.length),
113
+ updated: 0,
114
+ deleted: 0,
115
+ total: actual.length
116
+ };
117
+ } catch (error) {
118
+ const errorMessage = error instanceof Error ? error.message : String(error);
119
+ const errorStack = error instanceof Error ? error.stack : void 0;
120
+ throw new Error(`Error syncing app catalog: ${errorMessage}\n\nDetails:\n${errorStack || "No stack trace available"}`);
121
+ }
122
+ }
123
+
124
+ //#endregion
125
+ export { syncAppCatalog };
126
+ //# sourceMappingURL=syncAppCatalog.mjs.map
@@ -0,0 +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 } from '../types'\nimport type { PrismaClient } from '../generated/prisma/client'\n\nexport interface SyncAppCatalogResult {\n created: number\n updated: number\n deleted: number\n total: number\n}\n\ninterface AssetSyncResult {\n screenshotIds: Array<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<Array<string>> {\n try {\n const files = await readdir(dirPath)\n const assetIds: Array<string> = []\n\n for (let i = 0; i < files.length; i++) {\n const fileName = files[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: Array<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?: Array<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 // private async syncScreenshots(apps: AppForCatalog[], screenshotsPath: string): Promise<void> {\n // const db = getDbClient();\n //\n // for (const app of apps) {\n // const dirPath = join(screenshotsPath, `${app.slug}-screenshots`);\n //\n // try {\n // const files = await readdir(dirPath);\n // const imageFiles = files.filter(f => /\\.(png|jpg|jpeg|gif|webp)$/i.test(f)).sort();\n //\n // if (imageFiles.length === 0) continue;\n //\n // const screenshotIds: string[] = [];\n //\n // for (const filename of imageFiles) {\n // const filePath = join(dirPath, filename);\n // const content = await readFile(filePath);\n // const buffer = Buffer.from(content);\n // const checksum = generateChecksum(buffer);\n // const { width, height } = await getImageDimensions(buffer);\n //\n // const ext = filename.split('.').pop()?.toLowerCase();\n // const mimeType = {\n // png: 'image/png',\n // jpg: 'image/jpeg',\n // jpeg: 'image/jpeg',\n // gif: 'image/gif',\n // webp: 'image/webp',\n // }[ext || ''] || 'application/octet-stream';\n //\n // const asset = await db.dbAsset.upsert({\n // where: { checksum },\n // create: {\n // name: `${app.slug}/${filename}`,\n // assetType: 'screenshot',\n // content: new Uint8Array(buffer),\n // checksum,\n // mimeType,\n // fileSize: buffer.length,\n // width,\n // height,\n // },\n // update: {\n // name: `${app.slug}/${filename}`,\n // content: new Uint8Array(buffer),\n // mimeType,\n // fileSize: buffer.length,\n // width,\n // height,\n // },\n // });\n //\n // screenshotIds.push(asset.id);\n // }\n //\n // await db.dbAppForCatalog.update({\n // where: { slug: app.slug },\n // data: { screenshotIds },\n // });\n //\n // logger.info(`Synced ${screenshotIds.length} screenshots for ${app.slug}`);\n // } catch (error: any) {\n // if (error.code === 'ENOENT') continue; // Skip if directory doesn't exist\n // throw error;\n // }\n // }\n // }\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: Array<AppForCatalog>,\n tagsDefinitions: Array<GroupingTagDefinition>,\n approvalMethods: Array<ApprovalMethod>,\n sreenshotsPath?: string,\n): Promise<SyncAppCatalogResult> {\n try {\n const prisma = getDbClient()\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 // Transform AppForCatalog to DbAppForCatalog format\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 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 sources: app.sources ?? [],\n deprecated: app.deprecated ?? null,\n }\n })\n\n const result = await sync.sync(dbApps)\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":";;;;;;;;AAyBA,SAAS,oBAAoB,OAAyB;AACpD,QACE,iBAAiB,SACjB,UAAU,SACT,MAAgC,SAAS;;AAI9C,eAAe,sBACb,SACA,SACA,WACA,QACwB;AACxB,KAAI;EACF,MAAM,QAAQ,MAAM,QAAQ,QAAQ;EACpC,MAAM,WAA0B,EAAE;AAElC,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,WAAW,MAAM;AACvB,OAAI,CAAC,SAAU;GAEf,MAAM,YACJ,cAAc,eACV,GAAG,QAAQ,cAAc,IAAI,MAC7B,GAAG,QAAQ;GAEjB,MAAM,KAAK,MAAM,YAAY;IAC3B;IACA,QAAQ,MAAM,SAAS,GAAG,QAAQ,GAAG,WAAW;IAChD,kBAAkB;IAClB,MAAM;IACN;IACD,CAAC;AACF,YAAS,KAAK,GAAG;AAGjB,OAAI,cAAc,OAChB;;AAIJ,SAAO;UACA,OAAgB;AACvB,MAAI,oBAAoB,MAAM,CAC5B,QAAO,EAAE;AAEX,QAAM;;;AAIV,eAAe,cACb,SACA,SACA,QAC0B;AAe1B,QAAO;EACL,eAfoB,MAAM,sBAC1B,GAAG,QAAQ,eACX,SACA,cACA,OACD;EAWC,WATc,MAAM,sBACpB,GAAG,QAAQ,SACX,SACA,QACA,OACD,EAImB,SAAS,IAAI,GAAG,QAAQ,SAAS;EACpD;;AAGH,eAAe,yBACb,MACA,mBACA;CACA,MAAM,iBAAiB,MAAM,QAAQ,kBAAkB;CACvD,MAAM,SAAS,aAAa;CAC5B,MAAM,SAAS,MAAM,OAAO,MAAM,EAAE,KAAK;AAEzC,MAAK,MAAM,cAAc,gBAAgB;AACvC,MAAI;AAEF,OAAI,EADU,MAAM,KAAK,GAAG,kBAAkB,GAAG,aAAa,EACnD,aAAa,CACtB;WAEK,OAAgB;AACvB,OAAI,oBAAoB,MAAM,CAC5B;AAEF,SAAM;;EAGR,MAAM,UAAU;AAChB,MAAI,CAAC,OAAO,SACV,OAAM,IAAI,MACR,QAAQ,QAAQ,sDAAsD,OAAO,KAAK,OAAO,CAAC,KAAK,KAAK,GACrG;AAGH,MAAI;GACF,MAAM,EAAE,eAAe,aAAa,MAAM,cACxC,SACA,GAAG,kBAAkB,GAAG,cACxB,OACD;GAED,MAAM,aAGF,EAAE;AAEN,OAAI,cAAc,SAAS,EACzB,YAAW,gBAAgB;AAE7B,OAAI,aAAa,KACf,YAAW,WAAW;AAGxB,OAAI,OAAO,KAAK,WAAW,CAAC,SAAS,EACnC,OAAM,OAAO,gBAAgB,OAAO;IAClC,OAAO,EAAE,MAAM,SAAS;IACxB,MAAM;IACP,CAAC;WAEG,OAAgB;GACvB,MAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACxD,SAAM,IAAI,MACR,yCAAyC,QAAQ,KAAK,eACvD;;;;;;;;;;AA8EP,eAAsB,eACpB,MACA,iBACA,iBACA,gBAC+B;AAC/B,KAAI;EACF,MAAM,SAAS,aAAa;AAE5B,QAAM,gBAAgB;GACpB;GACA,GAAG,oBAAoB;GACxB,CAAC,CAAC,KAAK,gBAAgB;EAExB,MAAM,OAAO,gBAAgB;GAC3B;GACA,GAAG,oBAAoB;GACxB,CAAC;AAEF,QAAM,gBAAgB;GACpB;GACA,GAAG,oBAAoB;GACxB,CAAC,CAAC,KAAK,gBAAgB;EAGxB,MAAM,SAAS,KAAK,KAAK,QAAQ;AAQ/B,UAAO;IACL,MAPA,IAAI,QACJ,IAAI,YACD,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG;IAI1B,aAAa,IAAI;IACjB,aAAa,IAAI;IACjB,OAAO,IAAI,SAAS,EAAE;IACtB,eAAe,IAAI,iBAAiB;IACpC,OAAO,IAAI,SAAS;IACpB,MAAM,IAAI,QAAQ,EAAE;IACpB,QAAQ,IAAI,UAAU;IACtB,OAAO,IAAI,SAAS;IACpB,UAAU,IAAI,YAAY;IAC1B,eAAe,IAAI,iBAAiB,EAAE;IACtC,SAAS,IAAI,WAAW,EAAE;IAC1B,YAAY,IAAI,cAAc;IAC/B;IACD;EAKF,MAAM,UAHS,MAAM,KAAK,KAAK,OAAO,EAGhB,WAAW;AAEjC,MAAI,eACF,OAAM,yBAAyB,MAAM,eAAe;MAEpD,SAAQ,KAAK,yBAAyB;AAGxC,SAAO;GACL,SAAS,OAAO,SAAS,KAAK,UAAU,KAAK,SAAS,OAAO;GAC7D,SAAS;GACT,SAAS;GACT,OAAO,OAAO;GACf;UACM,OAAO;EAEd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;EAC3E,MAAM,aAAa,iBAAiB,QAAQ,MAAM,QAAQ;AAE1D,QAAM,IAAI,MACR,8BAA8B,aAAa,gBAAgB,cAAc,6BAC1E"}
@@ -0,0 +1,21 @@
1
+ //#region src/db/tableSyncMagazine.d.ts
2
+ declare const TABLE_SYNC_MAGAZINE: {
3
+ readonly DbAppForCatalog: {
4
+ readonly prismaModelName: "DbAppForCatalog";
5
+ readonly uniqColumns: ["slug"];
6
+ };
7
+ readonly DbAppTagDefinition: {
8
+ readonly prismaModelName: "DbAppTagDefinition";
9
+ readonly uniqColumns: ["prefix"];
10
+ };
11
+ readonly DbApprovalMethod: {
12
+ readonly id: "slug";
13
+ readonly prismaModelName: "DbApprovalMethod";
14
+ readonly uniqColumns: ["slug"];
15
+ };
16
+ };
17
+ type TableSyncMagazine = typeof TABLE_SYNC_MAGAZINE;
18
+ type TableSyncMagazineModelNameKey = keyof TableSyncMagazine;
19
+ //#endregion
20
+ export { TABLE_SYNC_MAGAZINE, TableSyncMagazine, TableSyncMagazineModelNameKey };
21
+ //# sourceMappingURL=tableSyncMagazine.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableSyncMagazine.d.mts","names":[],"sources":["../../src/db/tableSyncMagazine.ts"],"mappings":";cAca,mBAAA;EAAA;;;;;;;;;;;;;;KAgBD,iBAAA,UAA2B,mBAAA;AAAA,KAC3B,6BAAA,SAAsC,iBAAA"}
@@ -0,0 +1,20 @@
1
+ //#region src/db/tableSyncMagazine.ts
2
+ const TABLE_SYNC_MAGAZINE = {
3
+ DbAppForCatalog: {
4
+ prismaModelName: "DbAppForCatalog",
5
+ uniqColumns: ["slug"]
6
+ },
7
+ DbAppTagDefinition: {
8
+ prismaModelName: "DbAppTagDefinition",
9
+ uniqColumns: ["prefix"]
10
+ },
11
+ DbApprovalMethod: {
12
+ id: "slug",
13
+ prismaModelName: "DbApprovalMethod",
14
+ uniqColumns: ["slug"]
15
+ }
16
+ };
17
+
18
+ //#endregion
19
+ export { TABLE_SYNC_MAGAZINE };
20
+ //# sourceMappingURL=tableSyncMagazine.mjs.map
@@ -0,0 +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: Array<ScalarKeys<TPrismaModelName>>\n relationColumns?: Array<ObjectKeys<TPrismaModelName>>\n}\n\ntype TableSyncMagazineType = Partial<{\n [key in Prisma.ModelName]: CommonSyncTableInfo<key>\n}>\n\nexport const TABLE_SYNC_MAGAZINE = {\n DbAppForCatalog: {\n prismaModelName: 'DbAppForCatalog',\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} as const satisfies TableSyncMagazineType\n\nexport type TableSyncMagazine = typeof TABLE_SYNC_MAGAZINE\nexport type TableSyncMagazineModelNameKey = keyof TableSyncMagazine\n"],"mappings":";AAcA,MAAa,sBAAsB;CACjC,iBAAiB;EACf,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;CACF"}
@@ -0,0 +1,27 @@
1
+ import { ModelName, TypeMap } from "../generated/prisma/internal/prismaNamespace.mjs";
2
+ import { PrismaClient } from "../generated/prisma/client.mjs";
3
+
4
+ //#region src/db/tableSyncPrismaAdapter.d.ts
5
+ type ScalarKeys<TPrismaModelName extends ModelName> = keyof TypeMap['model'][TPrismaModelName]['payload']['scalars'];
6
+ type ObjectKeys<TPrismaModelName extends ModelName> = keyof TypeMap['model'][TPrismaModelName]['payload']['objects'];
7
+ type ScalarFilter<TPrismaModelName extends ModelName> = Partial<TypeMap['model'][TPrismaModelName]['payload']['scalars']>;
8
+ interface TableSyncParamsPrisma<TPrismaClient extends PrismaClient, TPrismaModelName extends ModelName, TUniqColumns extends ReadonlyArray<ScalarKeys<TPrismaModelName>>, TRelationColumns extends ReadonlyArray<ObjectKeys<TPrismaModelName>>> {
9
+ id?: ScalarKeys<TPrismaModelName>;
10
+ prisma: TPrismaClient;
11
+ prismaModelName: TPrismaModelName;
12
+ uniqColumns: TUniqColumns;
13
+ relationColumns?: TRelationColumns;
14
+ where?: ScalarFilter<TPrismaModelName>;
15
+ upsertOnly?: boolean;
16
+ }
17
+ type MakeTFromPrismaModel<TPrismaModelName extends ModelName> = NonNullable<TypeMap['model'][TPrismaModelName]['operations']['findUnique']['result']>;
18
+ declare function tableSyncPrisma<TPrismaClient extends PrismaClient, TPrismaModelName extends ModelName, TUniqColumns extends ReadonlyArray<ScalarKeys<TPrismaModelName>>, TRelationColumns extends ReadonlyArray<ObjectKeys<TPrismaModelName>>, TId extends ScalarKeys<TPrismaModelName> = ScalarKeys<TPrismaModelName>>(params: TableSyncParamsPrisma<TPrismaClient, TPrismaModelName, TUniqColumns, TRelationColumns>): {
19
+ sync<TUpsert extends Partial<MakeTFromPrismaModel<TPrismaModelName>> & Pick<MakeTFromPrismaModel<TPrismaModelName>, TUniqColumns[number]>>(upsertRaw: TUpsert[]): Promise<{
20
+ findIdMaybe: (key: Pick<MakeTFromPrismaModel<TPrismaModelName>, TUniqColumns[number]>) => MakeTFromPrismaModel<TPrismaModelName>[TId] | undefined;
21
+ findIdOrThrow: (key: Pick<MakeTFromPrismaModel<TPrismaModelName>, TUniqColumns[number]>) => MakeTFromPrismaModel<TPrismaModelName>[TId];
22
+ getActual(): (Pick<MakeTFromPrismaModel<TPrismaModelName>, TUniqColumns[number]> & MakeTFromPrismaModel<TPrismaModelName>[TId])[];
23
+ }>;
24
+ };
25
+ //#endregion
26
+ export { MakeTFromPrismaModel, ObjectKeys, ScalarFilter, ScalarKeys, TableSyncParamsPrisma, tableSyncPrisma };
27
+ //# sourceMappingURL=tableSyncPrismaAdapter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tableSyncPrismaAdapter.d.mts","names":[],"sources":["../../src/db/tableSyncPrismaAdapter.ts"],"mappings":";;;;KAIY,UAAA,0BAAoC,SAAA,UACxC,OAAA,UAAwB,gBAAA;AAAA,KACpB,UAAA,0BAAoC,SAAA,UACxC,OAAA,UAAwB,gBAAA;AAAA,KAEpB,YAAA,0BAAsC,SAAA,IAAoB,OAAA,CACpE,OAAA,UAAwB,gBAAA;AAAA,UAWT,qBAAA,uBACO,YAAA,2BACG,SAAA,uBACJ,aAAA,CAAc,UAAA,CAAW,gBAAA,6BACrB,aAAA,CAAc,UAAA,CAAW,gBAAA;EAElD,EAAA,GAAK,UAAA,CAAW,gBAAA;EAChB,MAAA,EAAQ,aAAA;EACR,eAAA,EAAiB,gBAAA;EACjB,WAAA,EAAa,YAAA;EACb,eAAA,GAAkB,gBAAA;EAClB,KAAA,GAAQ,YAAA,CAAa,gBAAA;EACrB,UAAA;AAAA;AAAA,KAYU,oBAAA,0BAA8C,SAAA,IACxD,WAAA,CACE,OAAA,UAAwB,gBAAA;AAAA,iBAGZ,eAAA,uBACQ,YAAA,2BACG,SAAA,uBACJ,aAAA,CAAc,UAAA,CAAW,gBAAA,6BACrB,aAAA,CAAc,UAAA,CAAW,gBAAA,gBACtC,UAAA,CAAW,gBAAA,IAAoB,UAAA,CAAW,gBAAA,EAAA,CAEtD,MAAA,EAAQ,qBAAA,CACN,aAAA,EACA,gBAAA,EACA,YAAA,EACA,gBAAA"}
@@ -0,0 +1,81 @@
1
+ import { mapValues, omit, pick } from "radashi";
2
+ import { tableSync } from "@igstack/app-catalog-table-sync";
3
+
4
+ //#region src/db/tableSyncPrismaAdapter.ts
5
+ function getPrismaModelOperations(prisma, prismaModelName) {
6
+ return prisma[prismaModelName.slice(0, 1).toLowerCase() + prismaModelName.slice(1)];
7
+ }
8
+ function tableSyncPrisma(params) {
9
+ const { prisma, prismaModelName, uniqColumns, where: whereGlobal, upsertOnly } = params;
10
+ const prismOperations = getPrismaModelOperations(prisma, prismaModelName);
11
+ const idColumn = params.id ?? "id";
12
+ return tableSync({
13
+ id: idColumn,
14
+ uniqColumns,
15
+ readAll: async () => {
16
+ const findManyArgs = whereGlobal ? { where: whereGlobal } : {};
17
+ return await prismOperations.findMany(findManyArgs);
18
+ },
19
+ writeAll: async (createData, update, deleteIds) => {
20
+ const prismaUniqKey = params.uniqColumns.join("_");
21
+ const relationColumnList = params.relationColumns ?? [];
22
+ return prisma.$transaction(async (tx) => {
23
+ const txOps = getPrismaModelOperations(tx, prismaModelName);
24
+ for (const { data, where } of update) {
25
+ const uniqKeyWhere = Object.keys(where).length > 1 ? { [prismaUniqKey]: where } : where;
26
+ const dataScalar = omit(data, relationColumnList);
27
+ const dataRelations = mapValues(pick(data, relationColumnList), (value) => {
28
+ return { set: value };
29
+ });
30
+ await txOps.update({
31
+ data: {
32
+ ...dataScalar,
33
+ ...dataRelations
34
+ },
35
+ where: { ...uniqKeyWhere }
36
+ });
37
+ }
38
+ if (upsertOnly !== true) await txOps.deleteMany({ where: { [idColumn]: { in: deleteIds } } });
39
+ const createDataMapped = createData.map((data) => {
40
+ const dataScalar = omit(data, relationColumnList);
41
+ const dataRelations = mapValues(pick(data, relationColumnList), (value) => {
42
+ return { connect: value };
43
+ });
44
+ return {
45
+ ...dataScalar,
46
+ ...dataRelations
47
+ };
48
+ });
49
+ if (createDataMapped.length > 0) {
50
+ const uniqKeysInCreate = /* @__PURE__ */ new Set();
51
+ const duplicateKeys = [];
52
+ for (const data of createDataMapped) {
53
+ const key = params.uniqColumns.map((col) => {
54
+ const value = data[col];
55
+ return value === null || value === void 0 ? "null" : String(value);
56
+ }).join(":");
57
+ if (uniqKeysInCreate.has(key)) duplicateKeys.push(key);
58
+ else uniqKeysInCreate.add(key);
59
+ }
60
+ if (duplicateKeys.length > 0) {
61
+ const uniqColumnsStr = params.uniqColumns.join(", ");
62
+ throw new Error(`Duplicate unique key values found in data to be created. Model: ${prismaModelName}, Unique columns: [${uniqColumnsStr}], Duplicate keys: [${duplicateKeys.join(", ")}]`);
63
+ }
64
+ }
65
+ const results = [];
66
+ if (relationColumnList.length === 0) {
67
+ const batchResult = await txOps.createManyAndReturn({ data: createDataMapped });
68
+ results.push(...batchResult);
69
+ } else for (const dataMappedElement of createDataMapped) {
70
+ const newVar = await txOps.create({ data: dataMappedElement });
71
+ results.push(newVar);
72
+ }
73
+ return results;
74
+ });
75
+ }
76
+ });
77
+ }
78
+
79
+ //#endregion
80
+ export { tableSyncPrisma };
81
+ //# sourceMappingURL=tableSyncPrismaAdapter.mjs.map
@@ -0,0 +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']['DbAppForCatalog']['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 ReadonlyArray<ScalarKeys<TPrismaModelName>>,\n TRelationColumns extends ReadonlyArray<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 ReadonlyArray<ScalarKeys<TPrismaModelName>>,\n TRelationColumns extends ReadonlyArray<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(findManyArgs)) as Array<\n MakeTFromPrismaModel<TPrismaModelName>\n >\n },\n writeAll: async (createData, update, deleteIds) => {\n const prismaUniqKey = params.uniqColumns.join('_')\n const relationColumnList =\n params.relationColumns ?? ([] as Array<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: Array<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: Array<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,SAAS,aAAa;;EAItD,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,gBAA+B,EAAE;AAEvC,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,UAAyD,EAAE;AAEjE,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"}
@@ -0,0 +1,26 @@
1
+ import { ApprovalMethodType, AssetType } from "./enums.mjs";
2
+ import { LogLevel, PrismaClientOptions } from "./internal/prismaNamespace.mjs";
3
+ import { PrismaClient as PrismaClient$1, PrismaClientConstructor } from "./internal/class.mjs";
4
+ import * as runtime from "@prisma/client/runtime/client";
5
+
6
+ //#region src/generated/prisma/client.d.ts
7
+ /**
8
+ * ## Prisma Client
9
+ *
10
+ * Type-safe database client for TypeScript
11
+ * @example
12
+ * ```
13
+ * const prisma = new PrismaClient({
14
+ * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
15
+ * })
16
+ * // Fetch zero or more Users
17
+ * const users = await prisma.user.findMany()
18
+ * ```
19
+ *
20
+ * Read more in our [docs](https://pris.ly/d/client).
21
+ */
22
+ declare const PrismaClient: PrismaClientConstructor;
23
+ type PrismaClient<LogOpts extends LogLevel = never, OmitOpts extends PrismaClientOptions["omit"] = PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = PrismaClient$1<LogOpts, OmitOpts, ExtArgs>;
24
+ //#endregion
25
+ export { PrismaClient };
26
+ //# sourceMappingURL=client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../../../src/generated/prisma/client.ts"],"mappings":";;;;;;;AAuCA;;;;;AACA;;;;;;;;;cADa,YAAA,EAAY,uBAAA;AAAA,KACb,YAAA,iBAA6B,QAAA,2BAA0C,mBAAA,WAAqC,mBAAA,0BAAoD,OAAA,CAAQ,KAAA,CAAM,UAAA,CAAW,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,UAAA,CAAW,WAAA,IAAe,cAAA,CAAoB,OAAA,EAAS,QAAA,EAAU,OAAA"}
@@ -0,0 +1,26 @@
1
+ import { getPrismaClientClass } from "./internal/class.mjs";
2
+ import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ //#region src/generated/prisma/client.ts
6
+ globalThis["__dirname"] = path.dirname(fileURLToPath(import.meta.url));
7
+ /**
8
+ * ## Prisma Client
9
+ *
10
+ * Type-safe database client for TypeScript
11
+ * @example
12
+ * ```
13
+ * const prisma = new PrismaClient({
14
+ * adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
15
+ * })
16
+ * // Fetch zero or more Users
17
+ * const users = await prisma.user.findMany()
18
+ * ```
19
+ *
20
+ * Read more in our [docs](https://pris.ly/d/client).
21
+ */
22
+ const PrismaClient = getPrismaClientClass();
23
+
24
+ //#endregion
25
+ export { PrismaClient };
26
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +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 DbApprovalMethod\n * \n */\nexport type DbApprovalMethod = Prisma.DbApprovalMethodModel\n/**\n * Model DbAppForCatalog\n * \n */\nexport type DbAppForCatalog = Prisma.DbAppForCatalogModel\n/**\n * Model DbAppTagDefinition\n * \n */\nexport type DbAppTagDefinition = Prisma.DbAppTagDefinitionModel\n/**\n * Model DbAsset\n * \n */\nexport type DbAsset = Prisma.DbAssetModel\n"],"mappings":";;;;;AAeA,WAAW,eAAe,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;AAwBtE,MAAa,eAAeA,sBAA6B"}