@cedarjs/cli 9.0.0-canary.1784 → 9.0.0-canary.3124

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 (277) hide show
  1. package/README.md +7 -10
  2. package/dist/cfw.js +10 -6
  3. package/dist/commands/build/buildHandler.js +220 -45
  4. package/dist/commands/build/buildPackagesTask.js +21 -20
  5. package/dist/commands/build.js +13 -2
  6. package/dist/commands/check.js +4 -6
  7. package/dist/commands/console.js +6 -4
  8. package/dist/commands/deploy/baremetal/SshExecutor.js +13 -4
  9. package/dist/commands/deploy/baremetal/baremetalHandler.js +116 -50
  10. package/dist/commands/deploy/baremetal.js +11 -4
  11. package/dist/commands/deploy/flightcontrolHandler.js +28 -15
  12. package/dist/commands/deploy/helpers/deployHandler.js +11 -6
  13. package/dist/commands/deploy/netlify.js +1 -1
  14. package/dist/commands/deploy/packing/nft.js +1 -2
  15. package/dist/commands/deploy/render.js +1 -1
  16. package/dist/commands/deploy/renderHandler.js +24 -13
  17. package/dist/commands/deploy/serverlessHandler.js +69 -27
  18. package/dist/commands/deploy/vercel.js +1 -1
  19. package/dist/commands/destroy/component/component.js +2 -3
  20. package/dist/commands/destroy/directive/directive.js +1 -3
  21. package/dist/commands/destroy/directive/directiveHandler.js +2 -4
  22. package/dist/commands/destroy/function/function.js +1 -3
  23. package/dist/commands/destroy/handlerHelpers.js +17 -17
  24. package/dist/commands/destroy/helpers.js +8 -2
  25. package/dist/commands/destroy/layout/layout.js +1 -2
  26. package/dist/commands/destroy/page/pageHandler.js +3 -3
  27. package/dist/commands/destroy/scaffold/scaffoldHandler.js +31 -12
  28. package/dist/commands/destroy/sdl/sdlHandler.js +3 -3
  29. package/dist/commands/dev/apiDebugFlag.js +5 -4
  30. package/dist/commands/dev/devHandler.js +219 -68
  31. package/dist/commands/dev/packageWatchCommands.js +8 -3
  32. package/dist/commands/dev.js +16 -6
  33. package/dist/commands/execHandler.js +49 -29
  34. package/dist/commands/experimental/live-queries/liveQueries.js +32 -0
  35. package/dist/commands/experimental/live-queries/liveQueriesHandler.js +448 -0
  36. package/dist/commands/experimental/live-queries/templates/liveQueriesListener.ts.template +153 -0
  37. package/dist/commands/experimental/live-queries/templates/migration.sql.template +100 -0
  38. package/dist/commands/experimental/setupInngest.js +2 -2
  39. package/dist/commands/experimental/setupInngestHandler.js +13 -12
  40. package/dist/commands/experimental/setupOpentelemetry.js +2 -2
  41. package/dist/commands/experimental/setupOpentelemetryHandler.js +14 -10
  42. package/dist/commands/experimental/setupReactCompiler.js +2 -2
  43. package/dist/commands/experimental/setupReactCompilerHandler.js +48 -33
  44. package/dist/commands/experimental/setupRsc.js +1 -1
  45. package/dist/commands/experimental/setupRscHandler.js +20 -16
  46. package/dist/commands/experimental/setupStreamingSsr.js +2 -2
  47. package/dist/commands/experimental/setupStreamingSsrHandler.js +49 -34
  48. package/dist/commands/experimental/templates/opentelemetry.ts.template +2 -1
  49. package/dist/commands/experimental/templates/rsc/Document.tsx.template +1 -1
  50. package/dist/commands/experimental/templates/rsc/entry.client.tsx.template +8 -8
  51. package/dist/commands/experimental/templates/streamingSsr/Document.tsx.template +1 -1
  52. package/dist/commands/experimental/templates/streamingSsr/entry.client.tsx.template +6 -6
  53. package/dist/commands/experimental/templates/streamingSsr/tsconfig.json.template +6 -6
  54. package/dist/commands/experimental/util.js +27 -25
  55. package/dist/commands/experimental.js +3 -2
  56. package/dist/commands/generate/cell/cell.js +20 -0
  57. package/dist/commands/generate/cell/cellHandler.js +89 -40
  58. package/dist/commands/generate/cell/templates/cell.tsx.template +21 -2
  59. package/dist/commands/generate/cell/templates/cellFragment.tsx.template +43 -0
  60. package/dist/commands/generate/cell/templates/cellList.tsx.template +21 -2
  61. package/dist/commands/generate/cell/templates/storiesFragment.tsx.template +23 -0
  62. package/dist/commands/generate/cell/templates/test.js.template +1 -1
  63. package/dist/commands/generate/cell/templates/testFragment.js.template +30 -0
  64. package/dist/commands/generate/component/componentHandler.js +14 -16
  65. package/dist/commands/generate/dataMigration/dataMigration.js +36 -18
  66. package/dist/commands/generate/dataMigration/templates/dataMigration.js.template +1 -1
  67. package/dist/commands/generate/dataMigration/templates/dataMigration.ts.template +1 -1
  68. package/dist/commands/generate/dbAuth/dbAuth.js +1 -0
  69. package/dist/commands/generate/dbAuth/dbAuthHandler.js +63 -34
  70. package/dist/commands/generate/dbAuth/templates/login.oauth.tsx.template +164 -0
  71. package/dist/commands/generate/dbAuth/templates/signup.oauth.tsx.template +147 -0
  72. package/dist/commands/generate/directive/directiveHandler.js +14 -16
  73. package/dist/commands/generate/function/function.js +1 -0
  74. package/dist/commands/generate/function/functionHandler.js +14 -19
  75. package/dist/commands/generate/helpers.js +34 -18
  76. package/dist/commands/generate/job/job.js +1 -0
  77. package/dist/commands/generate/job/jobHandler.js +20 -24
  78. package/dist/commands/generate/layout/layoutHandler.js +13 -16
  79. package/dist/commands/generate/model/model.js +1 -0
  80. package/dist/commands/generate/model/modelHandler.js +11 -5
  81. package/dist/commands/generate/ogImage/ogImage.js +1 -0
  82. package/dist/commands/generate/ogImage/ogImageHandler.js +18 -11
  83. package/dist/commands/generate/package/filesTask.js +9 -0
  84. package/dist/commands/generate/package/packageHandler.js +50 -61
  85. package/dist/commands/generate/package/templates/test.ts.template +2 -2
  86. package/dist/commands/generate/package/templates/vitest.config.ts.template +9 -0
  87. package/dist/commands/generate/page/pageHandler.js +27 -24
  88. package/dist/commands/generate/realtime/realtime.js +2 -2
  89. package/dist/commands/generate/realtime/realtimeHandler.js +51 -32
  90. package/dist/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template +1 -1
  91. package/dist/commands/generate/scaffold/scaffold.js +1 -0
  92. package/dist/commands/generate/scaffold/scaffoldHandler.js +176 -49
  93. package/dist/commands/generate/scaffold/templates/lib/formatters.tsx.template +1 -1
  94. package/dist/commands/generate/script/script.js +1 -0
  95. package/dist/commands/generate/script/scriptHandler.js +10 -6
  96. package/dist/commands/generate/script/templates/script.ts.template +9 -4
  97. package/dist/commands/generate/script/templates/tsconfig.json.template +16 -2
  98. package/dist/commands/generate/sdl/sdl.js +4 -3
  99. package/dist/commands/generate/sdl/sdlHandler.js +135 -46
  100. package/dist/commands/generate/sdl/stubFiles.js +132 -0
  101. package/dist/commands/generate/sdl/templates/sdl.js.template +2 -1
  102. package/dist/commands/generate/sdl/templates/sdl.ts.template +2 -1
  103. package/dist/commands/generate/secret/secret.js +20 -20
  104. package/dist/commands/generate/service/service.js +5 -5
  105. package/dist/commands/generate/service/serviceHandler.js +126 -61
  106. package/dist/commands/generate/service/templates/scenarios.ts.template +4 -2
  107. package/dist/commands/generate/service/templates/service.ts.template +22 -8
  108. package/dist/commands/generate/service/templates/test.ts.template +2 -2
  109. package/dist/commands/generate/yargsCommandHelpers.js +7 -2
  110. package/dist/commands/generate/yargsHandlerHelpers.js +20 -18
  111. package/dist/commands/generate.js +4 -4
  112. package/dist/commands/info.js +2 -2
  113. package/dist/commands/jobsHandler.js +7 -4
  114. package/dist/commands/lint.js +26 -69
  115. package/dist/commands/prerenderHandler.js +95 -58
  116. package/dist/commands/prismaHandler.js +51 -26
  117. package/dist/commands/record/init.js +2 -2
  118. package/dist/commands/record.js +4 -3
  119. package/dist/commands/serve.js +245 -31
  120. package/dist/commands/serveApiHandler.js +7 -6
  121. package/dist/commands/serveBothHandler.js +22 -13
  122. package/dist/commands/serveWebHandler.js +3 -2
  123. package/dist/commands/setup/auth/auth.js +15 -11
  124. package/dist/commands/setup/cache/cache.js +1 -1
  125. package/dist/commands/setup/cache/cacheHandler.js +15 -9
  126. package/dist/commands/setup/database/database.js +15 -0
  127. package/dist/commands/setup/database/postgres.js +19 -0
  128. package/dist/commands/setup/database/postgresHandler.js +196 -0
  129. package/dist/commands/setup/database/templates/db.ts.template +32 -0
  130. package/dist/commands/setup/deploy/deploy.js +4 -3
  131. package/dist/commands/setup/deploy/helpers/helpers.js +5 -1
  132. package/dist/commands/setup/deploy/helpers/index.js +132 -6
  133. package/dist/commands/setup/deploy/providers/baremetalHandler.js +28 -6
  134. package/dist/commands/setup/deploy/providers/coherence.js +1 -1
  135. package/dist/commands/setup/deploy/providers/coherenceHandler.js +60 -31
  136. package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +69 -51
  137. package/dist/commands/setup/deploy/providers/netlify.js +6 -0
  138. package/dist/commands/setup/deploy/providers/netlifyHandler.js +114 -13
  139. package/dist/commands/setup/deploy/providers/renderHandler.js +44 -41
  140. package/dist/commands/setup/deploy/providers/serverlessHandler.js +7 -5
  141. package/dist/commands/setup/deploy/providers/universal-deploy.js +11 -0
  142. package/dist/commands/setup/deploy/providers/universalDeployHandler.js +186 -0
  143. package/dist/commands/setup/deploy/providers/vercel.js +6 -0
  144. package/dist/commands/setup/deploy/providers/vercelHandler.js +115 -14
  145. package/dist/commands/setup/deploy/templates/baremetal.js +3 -2
  146. package/dist/commands/setup/deploy/templates/flightcontrol.js +60 -49
  147. package/dist/commands/setup/deploy/templates/netlify.js +2 -1
  148. package/dist/commands/setup/deploy/templates/netlifyUD.js +31 -0
  149. package/dist/commands/setup/deploy/templates/render.js +32 -17
  150. package/dist/commands/setup/docker/docker.js +2 -2
  151. package/dist/commands/setup/docker/dockerHandler.js +74 -39
  152. package/dist/commands/setup/docker/templates/Dockerfile.npm +119 -0
  153. package/dist/commands/setup/docker/templates/Dockerfile.pnpm +112 -0
  154. package/dist/commands/setup/docker/templates/{Dockerfile → Dockerfile.yarn} +4 -7
  155. package/dist/commands/setup/docker/templates/docker-compose.dev.yml +3 -3
  156. package/dist/commands/setup/docker/templates/docker-compose.prod.yml +2 -5
  157. package/dist/commands/setup/docker/templates/dockerignore +1 -0
  158. package/dist/commands/setup/generator/generator.js +1 -1
  159. package/dist/commands/setup/generator/generatorHandler.js +5 -4
  160. package/dist/commands/setup/graphql/features/fragments/appGqlConfigTransform.js +6 -3
  161. package/dist/commands/setup/graphql/features/fragments/fragments.js +1 -1
  162. package/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js +5 -7
  163. package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocuments.js +1 -1
  164. package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js +4 -3
  165. package/dist/commands/setup/i18n/i18n.js +1 -1
  166. package/dist/commands/setup/i18n/i18nHandler.js +21 -17
  167. package/dist/commands/setup/jobs/jobs.js +1 -1
  168. package/dist/commands/setup/jobs/jobsHandler.js +17 -12
  169. package/dist/commands/setup/mailer/mailer.js +1 -1
  170. package/dist/commands/setup/mailer/mailerHandler.js +13 -11
  171. package/dist/commands/setup/middleware/ogImage/ogImage.js +1 -1
  172. package/dist/commands/setup/middleware/ogImage/ogImageHandler.js +2 -1
  173. package/dist/commands/setup/monitoring/sentry/sentry.js +1 -1
  174. package/dist/commands/setup/monitoring/sentry/sentryHandler.js +18 -12
  175. package/dist/commands/setup/neon/neon.js +35 -0
  176. package/dist/commands/setup/neon/neonHandler.js +264 -0
  177. package/dist/commands/setup/package/package.js +1 -1
  178. package/dist/commands/setup/package/packageHandler.js +18 -14
  179. package/dist/commands/setup/realtime/addRealtimeToGraphql.js +1 -1
  180. package/dist/commands/setup/realtime/realtime.js +1 -1
  181. package/dist/commands/setup/realtime/realtimeHandler.js +26 -37
  182. package/dist/commands/setup/realtime/templates/realtime.ts.template +1 -1
  183. package/dist/commands/setup/realtime/templates/subscriptions/countdown/countdown.ts.template +1 -1
  184. package/dist/commands/setup/realtime/templates/subscriptions/newMessage/newMessage.ts.template +1 -1
  185. package/dist/commands/setup/server-file/serverFile.js +1 -1
  186. package/dist/commands/setup/server-file/serverFileHandler.js +18 -10
  187. package/dist/commands/setup/tenancy/authCodemod.js +66 -0
  188. package/dist/commands/setup/tenancy/dbCodemod.js +64 -0
  189. package/dist/commands/setup/tenancy/graphqlCodemod.js +88 -0
  190. package/dist/commands/setup/tenancy/preflight.js +49 -0
  191. package/dist/commands/setup/tenancy/schemaPrisma.js +81 -0
  192. package/dist/commands/setup/tenancy/signupCodemod.js +153 -0
  193. package/dist/commands/setup/tenancy/templates/InvitePage.tsx.template +63 -0
  194. package/dist/commands/setup/tenancy/templates/OrgScope.tsx.template +40 -0
  195. package/dist/commands/setup/tenancy/templates/ensureDefaultOrganizations.dataMigration.ts.template +39 -0
  196. package/dist/commands/setup/tenancy/templates/organizations.scenarios.ts.template +59 -0
  197. package/dist/commands/setup/tenancy/templates/organizations.sdl.ts.template +89 -0
  198. package/dist/commands/setup/tenancy/templates/organizations.test.ts.template +389 -0
  199. package/dist/commands/setup/tenancy/templates/organizations.ts.template +394 -0
  200. package/dist/commands/setup/tenancy/templates/requireMembership.test.ts.template +35 -0
  201. package/dist/commands/setup/tenancy/templates/requireMembership.ts.template +25 -0
  202. package/dist/commands/setup/tenancy/tenancy.js +30 -0
  203. package/dist/commands/setup/tenancy/tenancyHandler.js +425 -0
  204. package/dist/commands/setup/ui/helpers/helpers.js +5 -1
  205. package/dist/commands/setup/ui/libraries/chakra-uiHandler.js +16 -8
  206. package/dist/commands/setup/ui/libraries/mantineHandler.js +26 -16
  207. package/dist/commands/setup/ui/libraries/tailwindcssHandler.js +40 -29
  208. package/dist/commands/setup/uploads/schemaPrisma.js +41 -0
  209. package/dist/commands/setup/uploads/serverFile.js +151 -0
  210. package/dist/commands/setup/uploads/templates/requireUploadToken.ts.template +15 -0
  211. package/dist/commands/setup/uploads/templates/srcLibUploads.ts.template +23 -24
  212. package/dist/commands/setup/uploads/templates/uploads.sdl.ts.template +79 -0
  213. package/dist/commands/setup/uploads/templates/uploads.ts.template +89 -0
  214. package/dist/commands/setup/uploads/templates/withDataUri.ts.template +14 -0
  215. package/dist/commands/setup/uploads/templates/withSignedUrl.ts.template +16 -0
  216. package/dist/commands/setup/uploads/uploads.js +49 -7
  217. package/dist/commands/setup/uploads/uploadsHandler.js +266 -72
  218. package/dist/commands/setup.js +5 -4
  219. package/dist/commands/studio.js +1 -1
  220. package/dist/commands/studioHandler.js +2 -2
  221. package/dist/commands/test/datasourceWarning.js +42 -0
  222. package/dist/commands/test/testHandler.js +104 -0
  223. package/dist/commands/test/viteVersionCheck.js +75 -0
  224. package/dist/commands/test.js +16 -19
  225. package/dist/commands/type-check.js +2 -1
  226. package/dist/commands/type-checkHandler.js +25 -18
  227. package/dist/commands/upgrade/preUpgradeScripts.js +11 -11
  228. package/dist/commands/upgrade/upgrade.js +4 -2
  229. package/dist/commands/upgrade/upgradeHandler.js +97 -73
  230. package/dist/index.js +15 -11
  231. package/dist/lib/background.js +22 -1
  232. package/dist/lib/configureStorybook.js +2 -2
  233. package/dist/lib/exec.js +68 -63
  234. package/dist/lib/exit.js +20 -5
  235. package/dist/lib/extendFile.js +23 -13
  236. package/dist/lib/generatePrismaClient.js +48 -12
  237. package/dist/lib/index.js +137 -106
  238. package/dist/lib/loadEnvFiles.js +1 -1
  239. package/dist/lib/locking.js +8 -4
  240. package/dist/lib/merge/algorithms.js +4 -1
  241. package/dist/lib/merge/index.js +78 -41
  242. package/dist/lib/merge/semanticIdentity.js +18 -6
  243. package/dist/lib/merge/strategy.js +86 -36
  244. package/dist/lib/packages.js +23 -13
  245. package/dist/lib/plugin.js +13 -7
  246. package/dist/lib/pluralHelpers.js +6 -2
  247. package/dist/lib/project.js +3 -1
  248. package/dist/lib/rollback.js +7 -7
  249. package/dist/lib/schemaHelpers.js +16 -19
  250. package/dist/lib/test.js +20 -5
  251. package/dist/lib/updateCheck.js +47 -18
  252. package/dist/middleware/checkNodeVersion.js +9 -4
  253. package/dist/middleware/{detectProjectRxVersion.js → detectProjectCedarVersion.js} +2 -2
  254. package/dist/plugin.js +18 -12
  255. package/dist/telemetry/exporter.js +12 -5
  256. package/dist/telemetry/index.js +4 -3
  257. package/dist/telemetry/resource.js +9 -12
  258. package/dist/telemetry/send.js +14 -10
  259. package/dist/testLib/cells.js +5 -3
  260. package/package.json +44 -41
  261. package/LICENSE +0 -21
  262. package/dist/commands/consoleHandler.js +0 -68
  263. package/dist/commands/setup/tsconfig/tsconfig.js +0 -25
  264. package/dist/commands/setup/tsconfig/tsconfigHandler.js +0 -59
  265. package/dist/commands/setup/uploads/dbCodemod.js +0 -28
  266. package/dist/commands/setup/uploads/templates/signedUrl.ts.template +0 -21
  267. package/dist/commands/setup/vite/templates/vite.config.ts.template +0 -19
  268. package/dist/commands/setup/vite/vite.js +0 -38
  269. package/dist/commands/setup/vite/viteHandler.js +0 -83
  270. package/dist/commands/testEsm.js +0 -36
  271. package/dist/commands/testHandler.js +0 -137
  272. package/dist/commands/testHandlerEsm.js +0 -85
  273. package/dist/commands/ts-to-js.js +0 -20
  274. package/dist/lib/cedarPluralize.js +0 -56
  275. package/dist/lib/colors.js +0 -18
  276. package/dist/testLib/getFilesWithPattern.js +0 -21
  277. /package/dist/{index.d.js → global.d.js} +0 -0
@@ -0,0 +1,448 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { Listr } from "listr2";
4
+ import { colors as c } from "@cedarjs/cli-helpers/colors";
5
+ import {
6
+ addApiPackages,
7
+ addWebPackages
8
+ } from "@cedarjs/cli-helpers/installHelpers";
9
+ import { formatCedarCommand } from "@cedarjs/cli-helpers/packageManager/display";
10
+ import {
11
+ getConfigPath,
12
+ getMigrationsPath,
13
+ getSchemaPath
14
+ } from "@cedarjs/project-config";
15
+ import { errorTelemetry } from "@cedarjs/telemetry";
16
+ import { getPaths, transformTSToJS, writeFile } from "../../../lib/index.js";
17
+ import { isTypeScriptProject } from "../../../lib/project.js";
18
+ function getApiPackageJson() {
19
+ const apiPackageJsonPath = path.join(getPaths().api.base, "package.json");
20
+ return JSON.parse(fs.readFileSync(apiPackageJsonPath, "utf-8"));
21
+ }
22
+ function getWebPackageJson() {
23
+ const webPackageJsonPath = path.join(getPaths().web.base, "package.json");
24
+ return JSON.parse(fs.readFileSync(webPackageJsonPath, "utf-8"));
25
+ }
26
+ function hasPackage(packageJson, packageName) {
27
+ return Boolean(
28
+ packageJson.dependencies?.[packageName] || packageJson.devDependencies?.[packageName]
29
+ );
30
+ }
31
+ async function getPrismaProvider() {
32
+ try {
33
+ const prismaConfigPath = getPaths().api.prismaConfig;
34
+ if (!prismaConfigPath) {
35
+ return void 0;
36
+ }
37
+ let schemaPath = await getSchemaPath(prismaConfigPath);
38
+ if (!schemaPath) {
39
+ return void 0;
40
+ }
41
+ let stat;
42
+ try {
43
+ stat = fs.statSync(schemaPath);
44
+ } catch {
45
+ stat = void 0;
46
+ }
47
+ if (stat?.isDirectory()) {
48
+ const candidate = path.join(schemaPath, "schema.prisma");
49
+ if (fs.existsSync(candidate)) {
50
+ schemaPath = candidate;
51
+ } else {
52
+ return void 0;
53
+ }
54
+ }
55
+ if (!fs.existsSync(schemaPath)) {
56
+ return void 0;
57
+ }
58
+ const content = fs.readFileSync(schemaPath, "utf-8");
59
+ const match = content.match(/^\s*provider\s*=\s*["']([^"']+)["']/im);
60
+ if (match?.[1]) {
61
+ return match[1].toLowerCase();
62
+ }
63
+ return void 0;
64
+ } catch {
65
+ return void 0;
66
+ }
67
+ }
68
+ function findExistingLiveQueryMigration(migrationsDirectoryPath) {
69
+ if (!fs.existsSync(migrationsDirectoryPath)) {
70
+ return void 0;
71
+ }
72
+ const globPattern = path.join(migrationsDirectoryPath, "*", "migration.sql").replaceAll("\\", "/");
73
+ const migrationFilePaths = fs.globSync(globPattern);
74
+ return migrationFilePaths.find((migrationFilePath) => {
75
+ const content = fs.readFileSync(migrationFilePath, "utf-8");
76
+ return content.includes("cedar_notify_table_change");
77
+ });
78
+ }
79
+ function generateMigrationFolderName() {
80
+ const now = /* @__PURE__ */ new Date();
81
+ const year = String(now.getFullYear());
82
+ const month = String(now.getMonth() + 1).padStart(2, "0");
83
+ const day = String(now.getDate()).padStart(2, "0");
84
+ const hour = String(now.getHours()).padStart(2, "0");
85
+ const minute = String(now.getMinutes()).padStart(2, "0");
86
+ const second = String(now.getSeconds()).padStart(2, "0");
87
+ return `${year}${month}${day}${hour}${minute}${second}_live_queries_notifications`;
88
+ }
89
+ function addLiveQueryListenerToGraphqlHandler({ force }) {
90
+ const graphqlHandlerPath = path.join(
91
+ getPaths().api.functions,
92
+ `graphql.${isTypeScriptProject() ? "ts" : "js"}`
93
+ );
94
+ if (!fs.existsSync(graphqlHandlerPath)) {
95
+ return {
96
+ skipped: true,
97
+ reason: "GraphQL handler not found"
98
+ };
99
+ }
100
+ const contentLines = fs.readFileSync(graphqlHandlerPath, "utf-8").split("\n");
101
+ const importLineRegex = /^import {.*startLiveQueryListener.*} from ['"]src\/lib\/liveQueriesListener['"];?$/;
102
+ const multilineImportRegex = /^} from ['"]src\/lib\/liveQueriesListener['"];?$/;
103
+ const hasImport = contentLines.some((line) => {
104
+ return importLineRegex.test(line) || multilineImportRegex.test(line);
105
+ });
106
+ const hasStartCall = contentLines.some(
107
+ (line) => line.trim().startsWith("startLiveQueryListener(") || line.trim().startsWith("void startLiveQueryListener(")
108
+ );
109
+ if (hasImport && hasStartCall && !force) {
110
+ return {
111
+ skipped: true,
112
+ reason: "Listener is already wired into GraphQL handler"
113
+ };
114
+ }
115
+ const handlerIndex = contentLines.findLastIndex(
116
+ (line) => line === "export const handler = createGraphQLHandler({"
117
+ );
118
+ if (handlerIndex === -1) {
119
+ return {
120
+ skipped: true,
121
+ reason: "Unexpected syntax. Handler not found"
122
+ };
123
+ }
124
+ const lastImportIndex = contentLines.slice(0, handlerIndex).findLastIndex((line) => line.startsWith("import "));
125
+ if (lastImportIndex === -1) {
126
+ return {
127
+ skipped: true,
128
+ reason: "Unexpected syntax. No imports found"
129
+ };
130
+ }
131
+ if (!hasImport) {
132
+ const loggerImportIndex = contentLines.findIndex(
133
+ (line) => /import { logger } from ['"]src\/lib\/logger['"]/.test(line)
134
+ );
135
+ const insertIndex = loggerImportIndex >= 0 ? loggerImportIndex : lastImportIndex + 1;
136
+ contentLines.splice(
137
+ insertIndex,
138
+ 0,
139
+ "import { startLiveQueryListener } from 'src/lib/liveQueriesListener'"
140
+ );
141
+ }
142
+ const handlerIndexAfterImport = hasImport ? handlerIndex : handlerIndex + 1;
143
+ if (!hasStartCall) {
144
+ contentLines.splice(
145
+ handlerIndexAfterImport,
146
+ 0,
147
+ "// Fire-and-forget: we intentionally don't await this so it doesn't block the",
148
+ "// GraphQL handler from being registered. The listener doesn't need to be ready",
149
+ "// before the first request is handled.",
150
+ "void startLiveQueryListener()",
151
+ ""
152
+ );
153
+ }
154
+ fs.writeFileSync(graphqlHandlerPath, contentLines.join("\n"));
155
+ return {
156
+ skipped: false
157
+ };
158
+ }
159
+ function addConfigureGqlormToApp({ force }) {
160
+ const appPath = getPaths().web.app;
161
+ if (!fs.existsSync(appPath)) {
162
+ return {
163
+ skipped: true,
164
+ reason: `${path.basename(appPath)} not found`
165
+ };
166
+ }
167
+ const content = fs.readFileSync(appPath, "utf-8");
168
+ const contentLines = content.split("\n");
169
+ const hasGqlormImport = contentLines.some(
170
+ (line) => line.includes("from '@cedarjs/gqlorm/setup'")
171
+ );
172
+ const hasSchemaImport = contentLines.some(
173
+ (line) => line.includes("from '../../.cedar/gqlorm-schema.json'")
174
+ );
175
+ const hasConfigureCall = contentLines.some(
176
+ (line) => line.includes("configureGqlorm({ schema })")
177
+ );
178
+ if (hasGqlormImport && hasSchemaImport && hasConfigureCall && !force) {
179
+ return {
180
+ skipped: true,
181
+ reason: "configureGqlorm is already wired into App"
182
+ };
183
+ }
184
+ if (!hasGqlormImport) {
185
+ const redwoodWebImportIndex = contentLines.findIndex(
186
+ (line) => line.includes("from '@cedarjs/web'")
187
+ );
188
+ if (redwoodWebImportIndex === -1) {
189
+ return {
190
+ skipped: true,
191
+ reason: "Unexpected syntax. Could not find @cedarjs/web import to insert gqlorm import"
192
+ };
193
+ }
194
+ contentLines.splice(
195
+ redwoodWebImportIndex,
196
+ 0,
197
+ "import { configureGqlorm } from '@cedarjs/gqlorm/setup'"
198
+ );
199
+ }
200
+ if (!hasSchemaImport) {
201
+ const fatalErrorPageIndex = contentLines.findIndex(
202
+ (line) => line.includes("import FatalErrorPage from 'src/pages/FatalErrorPage'")
203
+ );
204
+ if (fatalErrorPageIndex === -1) {
205
+ return {
206
+ skipped: true,
207
+ reason: "Unexpected syntax. Could not find FatalErrorPage import to insert schema import"
208
+ };
209
+ }
210
+ contentLines.splice(
211
+ fatalErrorPageIndex + 1,
212
+ 0,
213
+ "import schema from '../../.cedar/gqlorm-schema.json' with { type: 'json' }"
214
+ );
215
+ }
216
+ if (!hasConfigureCall) {
217
+ const appComponentIndex = contentLines.findIndex(
218
+ (line) => /^const App\s*=/.test(line)
219
+ );
220
+ if (appComponentIndex === -1) {
221
+ return {
222
+ skipped: true,
223
+ reason: "Unexpected syntax. Could not find `const App =` to insert configureGqlorm call"
224
+ };
225
+ }
226
+ contentLines.splice(appComponentIndex, 0, "configureGqlorm({ schema })", "");
227
+ }
228
+ fs.writeFileSync(appPath, contentLines.join("\n"));
229
+ return {
230
+ skipped: false
231
+ };
232
+ }
233
+ async function handler({ force, verbose }) {
234
+ const projectIsTypescript = isTypeScriptProject();
235
+ const apiPackageJson = getApiPackageJson();
236
+ const migrationsPath = await getMigrationsPath(getPaths().api.prismaConfig);
237
+ const hasRealtimeDependency = hasPackage(apiPackageJson, "@cedarjs/realtime");
238
+ const hasPgDependency = hasPackage(apiPackageJson, "pg");
239
+ const webPackageJson = getWebPackageJson();
240
+ const hasGqlormDependency = hasPackage(webPackageJson, "@cedarjs/gqlorm");
241
+ const ext = projectIsTypescript ? "ts" : "js";
242
+ const migrationTemplatePath = path.resolve(
243
+ import.meta.dirname,
244
+ "templates",
245
+ "migration.sql.template"
246
+ );
247
+ const listenerTemplatePath = path.resolve(
248
+ import.meta.dirname,
249
+ "templates",
250
+ "liveQueriesListener.ts.template"
251
+ );
252
+ const existingMigrationPath = findExistingLiveQueryMigration(migrationsPath);
253
+ const migrationDirPath = path.join(
254
+ migrationsPath,
255
+ generateMigrationFolderName()
256
+ );
257
+ const migrationPath = path.join(migrationDirPath, "migration.sql");
258
+ const listenerPath = path.join(
259
+ getPaths().api.lib,
260
+ `liveQueriesListener.${ext}`
261
+ );
262
+ const tasks = new Listr(
263
+ [
264
+ {
265
+ title: "Checking for @cedarjs/realtime in api workspace...",
266
+ task: () => {
267
+ if (!hasRealtimeDependency) {
268
+ throw new Error(
269
+ `@cedarjs/realtime is not installed in your api workspace. Please run ${c.highlight(formatCedarCommand(["setup", "realtime"]))} first.`
270
+ );
271
+ }
272
+ }
273
+ },
274
+ {
275
+ title: "Checking that your database provider is PostgreSQL...",
276
+ task: async () => {
277
+ const prismaProvider = await getPrismaProvider();
278
+ const unsupportedProviders = /* @__PURE__ */ new Set([
279
+ "sqlite",
280
+ "mysql",
281
+ "mongodb",
282
+ "sqlserver",
283
+ "cockroachdb"
284
+ ]);
285
+ if (prismaProvider && unsupportedProviders.has(prismaProvider)) {
286
+ throw new Error(
287
+ `Only PostgreSQL is supported for now (found provider "${prismaProvider}").`
288
+ );
289
+ }
290
+ }
291
+ },
292
+ {
293
+ title: "Adding [experimental.gqlorm] config...",
294
+ task: (_ctx, task) => {
295
+ const configTomlPath = getConfigPath();
296
+ const configFileName = path.basename(configTomlPath);
297
+ const configContent = fs.readFileSync(configTomlPath, "utf-8");
298
+ if (!configContent.includes("[experimental.gqlorm]")) {
299
+ writeFile(
300
+ configTomlPath,
301
+ configContent.concat(
302
+ "\n[experimental.gqlorm]\n enabled = true\n"
303
+ ),
304
+ {
305
+ overwriteExisting: true
306
+ }
307
+ );
308
+ } else {
309
+ if (force) {
310
+ task.output = `Overwriting config in ${configFileName}`;
311
+ writeFile(
312
+ configTomlPath,
313
+ configContent.replace(
314
+ "\n[experimental.gqlorm]\n enabled = false\n",
315
+ "\n[experimental.gqlorm]\n enabled = true\n"
316
+ ),
317
+ {
318
+ overwriteExisting: true
319
+ }
320
+ );
321
+ } else {
322
+ task.skip(
323
+ `The [experimental.gqlorm] config block already exists in ${configFileName}.`
324
+ );
325
+ }
326
+ }
327
+ },
328
+ rendererOptions: { persistentOutput: true }
329
+ },
330
+ {
331
+ ...addApiPackages(["pg@^8.18.0"]),
332
+ title: "Adding pg dependency to your api workspace...",
333
+ skip: () => {
334
+ if (hasPgDependency) {
335
+ return "pg is already installed";
336
+ }
337
+ return false;
338
+ }
339
+ },
340
+ {
341
+ title: "Adding live query notification migration...",
342
+ task: () => {
343
+ const migrationTemplate = fs.readFileSync(
344
+ migrationTemplatePath,
345
+ "utf-8"
346
+ );
347
+ const targetPath = force && existingMigrationPath ? existingMigrationPath : migrationPath;
348
+ writeFile(targetPath, migrationTemplate, {
349
+ overwriteExisting: force
350
+ });
351
+ },
352
+ skip: () => {
353
+ if (existingMigrationPath && !force) {
354
+ const migrationPath2 = path.relative(
355
+ getPaths().base,
356
+ existingMigrationPath
357
+ );
358
+ return `Existing live query migration found: ${migrationPath2}`;
359
+ }
360
+ return false;
361
+ }
362
+ },
363
+ {
364
+ title: `Adding api/src/lib/liveQueriesListener.${ext}...`,
365
+ task: async () => {
366
+ const listenerTemplate = fs.readFileSync(
367
+ listenerTemplatePath,
368
+ "utf-8"
369
+ );
370
+ const listenerContent = projectIsTypescript ? listenerTemplate : await transformTSToJS(listenerPath, listenerTemplate);
371
+ writeFile(listenerPath, listenerContent, {
372
+ overwriteExisting: force
373
+ });
374
+ }
375
+ },
376
+ {
377
+ title: "Wiring listener startup into GraphQL handler...",
378
+ task: (_ctx, task) => {
379
+ const result = addLiveQueryListenerToGraphqlHandler({ force });
380
+ if (result.skipped) {
381
+ task.skip(result.reason);
382
+ }
383
+ }
384
+ },
385
+ {
386
+ ...addWebPackages(["@cedarjs/gqlorm"]),
387
+ title: "Adding @cedarjs/gqlorm to your web workspace...",
388
+ skip: () => {
389
+ if (hasGqlormDependency) {
390
+ return "@cedarjs/gqlorm is already installed";
391
+ }
392
+ return false;
393
+ }
394
+ },
395
+ {
396
+ title: "Wiring configureGqlorm into App.tsx...",
397
+ task: (_ctx, task) => {
398
+ const result = addConfigureGqlormToApp({ force });
399
+ if (result.skipped) {
400
+ task.skip(result.reason);
401
+ }
402
+ }
403
+ },
404
+ {
405
+ title: "One more thing...",
406
+ task: (_ctx, task) => {
407
+ task.title = `One more thing...
408
+
409
+ ${c.success("\nLive query notifications configured!\n")}
410
+
411
+ Apply the migration to activate Postgres notifications:
412
+ ${c.highlight(`
413
+ \xA0\xA0${formatCedarCommand(["prisma", "migrate", "dev"])}
414
+ `)}
415
+
416
+ You're then ready to use @live queries to get real-time updates as
417
+ soon as something in your database changes.
418
+ `;
419
+ }
420
+ }
421
+ ],
422
+ {
423
+ rendererOptions: { collapseSubtasks: false },
424
+ renderer: verbose ? "verbose" : "default"
425
+ }
426
+ );
427
+ try {
428
+ await tasks.run();
429
+ } catch (e) {
430
+ if (isObject(e) && "message" in e) {
431
+ errorTelemetry(process.argv, e.message);
432
+ console.error(c.error(e.message));
433
+ } else {
434
+ errorTelemetry(process.argv, e);
435
+ console.error(c.error(e));
436
+ }
437
+ process.exit(isObjectWithExitCode(e) ? e.exitCode : 1);
438
+ }
439
+ }
440
+ function isObject(value) {
441
+ return !!value && typeof value === "object" && !Array.isArray(value);
442
+ }
443
+ function isObjectWithExitCode(value) {
444
+ return isObject(value) && typeof value.exitCode === "number";
445
+ }
446
+ export {
447
+ handler
448
+ };
@@ -0,0 +1,153 @@
1
+ import { Client } from 'pg'
2
+
3
+ import { getPaths, loadPrismaConfig } from '@cedarjs/project-config'
4
+ import { liveQueryStore } from '@cedarjs/realtime'
5
+ import { pluralize } from '@cedarjs/utils/cedarPluralize'
6
+
7
+ import { logger } from 'src/lib/logger'
8
+
9
+ const LIVE_QUERY_CHANNEL = 'table_change'
10
+ const RECONNECT_DELAY_MS = 5000
11
+
12
+ let client: Client | undefined
13
+ let reconnectTimeout: ReturnType<typeof setTimeout> | undefined
14
+ let started = false
15
+ let connectionGeneration = 0
16
+
17
+ interface GetKeysToInvalidateArgs {
18
+ table: string
19
+ recordId: string
20
+ }
21
+
22
+ function getKeysToInvalidate({ table, recordId }: GetKeysToInvalidateArgs) {
23
+ // Using `toLowerCase()` rather than `toLocaleLowerCase()` because we want
24
+ // consistent casing across all locales.
25
+ const typeName = table.charAt(0).toLowerCase() + table.slice(1)
26
+
27
+ const keys = [
28
+ `Query.${typeName}`,
29
+ `Query.${pluralize(typeName)}`,
30
+ `${typeName}:${recordId}`,
31
+ ]
32
+
33
+ return keys
34
+ }
35
+
36
+ function reconnect(generation: number) {
37
+ if (reconnectTimeout) {
38
+ return
39
+ }
40
+
41
+ reconnectTimeout = setTimeout(async () => {
42
+ reconnectTimeout = undefined
43
+
44
+ if (generation !== connectionGeneration) {
45
+ return
46
+ }
47
+
48
+ await connect()
49
+ }, RECONNECT_DELAY_MS)
50
+ }
51
+
52
+ interface NotificationPayload {
53
+ table: string
54
+ operation: string
55
+ recordId: string
56
+ }
57
+
58
+ async function onNotification(payload: string | undefined) {
59
+ if (!payload) {
60
+ return
61
+ }
62
+
63
+ try {
64
+ const parsed: NotificationPayload = JSON.parse(payload)
65
+ const keys = getKeysToInvalidate(parsed)
66
+
67
+ await liveQueryStore?.invalidate(keys)
68
+
69
+ logger.debug(
70
+ { operation: parsed.operation, table: parsed.table, keys },
71
+ 'Invalidated live query keys from Postgres notification'
72
+ )
73
+ } catch (error) {
74
+ logger.error(
75
+ { error, payload },
76
+ 'Failed to process Postgres notification payload'
77
+ )
78
+ }
79
+ }
80
+
81
+ async function connect() {
82
+ connectionGeneration = connectionGeneration + 1
83
+ const generation = connectionGeneration
84
+ const prismaConfigPath = getPaths().api.prismaConfig
85
+ const prismaConfig = await loadPrismaConfig(prismaConfigPath)
86
+ const prismaDatasourceUrl = prismaConfig.datasource?.url
87
+
88
+ if (!prismaDatasourceUrl) {
89
+ const config = JSON.stringify(prismaConfig, null, 2)
90
+
91
+ throw new Error(
92
+ 'Could not determine Postgres connection URL from Prisma config ' +
93
+ `datasource. Using parsed Prisma config: ${config}`
94
+ )
95
+ }
96
+
97
+ try {
98
+ if (reconnectTimeout) {
99
+ clearTimeout(reconnectTimeout)
100
+ reconnectTimeout = undefined
101
+ }
102
+
103
+ if (client) {
104
+ const previousClient = client
105
+ client = undefined
106
+ previousClient.removeAllListeners()
107
+ await previousClient.end().catch(() => {})
108
+ }
109
+
110
+ const nextClient = new Client({ connectionString: prismaDatasourceUrl })
111
+
112
+ nextClient.on('notification', async (msg) => {
113
+ await onNotification(msg.payload)
114
+ })
115
+
116
+ nextClient.on('error', (error) => {
117
+ logger.error(
118
+ { error },
119
+ 'Postgres live query listener encountered an error'
120
+ )
121
+ reconnect(generation)
122
+ })
123
+
124
+ nextClient.on('end', () => {
125
+ logger.warn('Postgres live query listener disconnected')
126
+ reconnect(generation)
127
+ })
128
+
129
+ await nextClient.connect()
130
+ await nextClient.query(`LISTEN ${LIVE_QUERY_CHANNEL}`)
131
+
132
+ if (generation !== connectionGeneration) {
133
+ nextClient.removeAllListeners()
134
+ await nextClient.end().catch(() => {})
135
+ return
136
+ }
137
+
138
+ client = nextClient
139
+ logger.info('Postgres live query listener connected')
140
+ } catch (error) {
141
+ logger.error({ error }, 'Failed to connect Postgres live query listener')
142
+ reconnect(generation)
143
+ }
144
+ }
145
+
146
+ export async function startLiveQueryListener() {
147
+ if (started) {
148
+ return
149
+ }
150
+
151
+ started = true
152
+ await connect()
153
+ }
@@ -0,0 +1,100 @@
1
+ CREATE OR REPLACE FUNCTION cedar_notify_table_change() RETURNS TRIGGER AS $$
2
+ DECLARE
3
+ record_id text;
4
+ BEGIN
5
+ IF (TG_OP = 'DELETE') THEN
6
+ record_id := OLD.id::text;
7
+ ELSE
8
+ record_id := NEW.id::text;
9
+ END IF;
10
+
11
+ PERFORM pg_notify(
12
+ 'table_change',
13
+ json_build_object(
14
+ 'table', TG_TABLE_NAME,
15
+ 'operation', TG_OP,
16
+ 'recordId', record_id
17
+ )::text
18
+ );
19
+
20
+ RETURN NULL;
21
+ END;
22
+ $$ LANGUAGE plpgsql;
23
+
24
+ CREATE OR REPLACE FUNCTION cedar_attach_notify_triggers() RETURNS void AS $$
25
+ DECLARE
26
+ table_record record;
27
+ BEGIN
28
+ FOR table_record IN
29
+ SELECT table_schema, table_name
30
+ FROM information_schema.tables
31
+ WHERE table_type = 'BASE TABLE'
32
+ AND table_schema NOT IN ('pg_catalog', 'information_schema')
33
+ AND table_name <> '_prisma_migrations'
34
+ LOOP
35
+ EXECUTE format(
36
+ 'DROP TRIGGER IF EXISTS cedar_notify_change_trigger ON %I.%I',
37
+ table_record.table_schema,
38
+ table_record.table_name
39
+ );
40
+
41
+ EXECUTE format(
42
+ 'CREATE TRIGGER cedar_notify_change_trigger
43
+ AFTER INSERT OR UPDATE OR DELETE ON %I.%I
44
+ FOR EACH ROW EXECUTE FUNCTION cedar_notify_table_change()',
45
+ table_record.table_schema,
46
+ table_record.table_name
47
+ );
48
+ END LOOP;
49
+ END;
50
+ $$ LANGUAGE plpgsql;
51
+
52
+ SELECT cedar_attach_notify_triggers();
53
+
54
+ CREATE OR REPLACE FUNCTION cedar_event_on_table_create() RETURNS event_trigger AS $$
55
+ DECLARE
56
+ cmd record;
57
+ schema_name text;
58
+ table_name text;
59
+ ident_parts text[];
60
+ BEGIN
61
+ FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
62
+ IF cmd.object_type = 'table' THEN
63
+ schema_name := cmd.schema_name;
64
+ ident_parts := parse_ident(cmd.object_identity);
65
+ table_name := ident_parts[array_length(ident_parts, 1)];
66
+
67
+ IF schema_name IS NULL THEN
68
+ CONTINUE;
69
+ END IF;
70
+
71
+ IF schema_name IN ('pg_catalog', 'information_schema') THEN
72
+ CONTINUE;
73
+ END IF;
74
+
75
+ IF table_name = '_prisma_migrations' THEN
76
+ CONTINUE;
77
+ END IF;
78
+
79
+ EXECUTE format(
80
+ 'DROP TRIGGER IF EXISTS cedar_notify_change_trigger ON %I.%I',
81
+ schema_name,
82
+ table_name
83
+ );
84
+
85
+ EXECUTE format(
86
+ 'CREATE TRIGGER cedar_notify_change_trigger
87
+ AFTER INSERT OR UPDATE OR DELETE ON %I.%I
88
+ FOR EACH ROW EXECUTE FUNCTION cedar_notify_table_change()',
89
+ schema_name,
90
+ table_name
91
+ );
92
+ END IF;
93
+ END LOOP;
94
+ END;
95
+ $$ LANGUAGE plpgsql;
96
+
97
+ CREATE EVENT TRIGGER cedar_on_create_table
98
+ ON ddl_command_end
99
+ WHEN TAG IN ('CREATE TABLE', 'CREATE TABLE AS')
100
+ EXECUTE FUNCTION cedar_event_on_table_create();
@@ -1,8 +1,8 @@
1
- import { recordTelemetryAttributes } from "@cedarjs/cli-helpers";
1
+ import { recordTelemetryAttributes } from "@cedarjs/cli-helpers/telemetry";
2
2
  import { getEpilogue } from "./util.js";
3
3
  const command = "setup-inngest";
4
4
  const description = "Setup Inngest for background, scheduled, delayed, multi-step, and fan-out jobs";
5
- const EXPERIMENTAL_TOPIC_ID = 4866;
5
+ const EXPERIMENTAL_TOPIC_ID = "4866";
6
6
  const builder = (yargs) => {
7
7
  yargs.option("force", {
8
8
  alias: "f",