@codenotch/codenotch.cli 1.0.35 → 1.0.37

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 (149) hide show
  1. package/README.md +2 -2
  2. package/dist/commands/generate/app.d.ts +2 -0
  3. package/dist/commands/generate/app.js +45 -0
  4. package/dist/commands/generate/app.js.map +1 -0
  5. package/dist/commands/generate/appmanifest.d.ts +2 -0
  6. package/dist/commands/generate/appmanifest.js +34 -0
  7. package/dist/commands/generate/appmanifest.js.map +1 -0
  8. package/dist/commands/generate/dbcontext.d.ts +2 -0
  9. package/dist/commands/generate/dbcontext.js +32 -0
  10. package/dist/commands/generate/dbcontext.js.map +1 -0
  11. package/dist/commands/generate/i18n.d.ts +2 -0
  12. package/dist/commands/generate/i18n.js +29 -0
  13. package/dist/commands/generate/i18n.js.map +1 -0
  14. package/dist/commands/generate/index.d.ts +2 -0
  15. package/dist/commands/generate/index.js +23 -0
  16. package/dist/commands/generate/index.js.map +1 -0
  17. package/dist/commands/generate/manifest.d.ts +2 -0
  18. package/dist/commands/generate/manifest.js +29 -0
  19. package/dist/commands/generate/manifest.js.map +1 -0
  20. package/dist/commands/generate/openapi.d.ts +2 -0
  21. package/dist/commands/generate/openapi.js +32 -0
  22. package/dist/commands/generate/openapi.js.map +1 -0
  23. package/dist/commands/generate/pkg.d.ts +2 -0
  24. package/dist/commands/generate/pkg.js +34 -0
  25. package/dist/commands/generate/pkg.js.map +1 -0
  26. package/dist/commands/generate/shared.d.ts +17 -0
  27. package/dist/commands/generate/shared.js +70 -0
  28. package/dist/commands/generate/shared.js.map +1 -0
  29. package/dist/commands/index.js +2 -0
  30. package/dist/commands/index.js.map +1 -1
  31. package/dist/commands/project/new.js +107 -187
  32. package/dist/commands/project/new.js.map +1 -1
  33. package/dist/commands/react/build.js +11 -5
  34. package/dist/commands/react/build.js.map +1 -1
  35. package/dist/commands/react/clean.d.ts +2 -0
  36. package/dist/commands/react/clean.js +46 -0
  37. package/dist/commands/react/clean.js.map +1 -0
  38. package/dist/commands/react/dev.d.ts +2 -0
  39. package/dist/commands/react/dev.js +27 -0
  40. package/dist/commands/react/dev.js.map +1 -0
  41. package/dist/commands/react/index.js +5 -1
  42. package/dist/commands/react/index.js.map +1 -1
  43. package/dist/commands/react/serve.js +33 -4
  44. package/dist/commands/react/serve.js.map +1 -1
  45. package/dist/commands/react/shared.d.ts +21 -0
  46. package/dist/commands/react/shared.js +80 -0
  47. package/dist/commands/react/shared.js.map +1 -0
  48. package/dist/commands/system/info.js +0 -7
  49. package/dist/commands/system/info.js.map +1 -1
  50. package/dist/commands/validation/ValidationUtils.d.ts +3 -13
  51. package/dist/commands/validation/ValidationUtils.js +5 -58
  52. package/dist/commands/validation/ValidationUtils.js.map +1 -1
  53. package/dist/commands/validation/files/AppManifestValidator.js +5 -20
  54. package/dist/commands/validation/files/AppManifestValidator.js.map +1 -1
  55. package/dist/commands/validation/files/I18nValidator.d.ts +24 -0
  56. package/dist/commands/validation/files/I18nValidator.js +190 -5
  57. package/dist/commands/validation/files/I18nValidator.js.map +1 -1
  58. package/dist/commands/validation/files/{BpmnValidator.d.ts → ProjectManifestValidator.d.ts} +1 -1
  59. package/dist/commands/validation/files/ProjectManifestValidator.js +31 -0
  60. package/dist/commands/validation/files/ProjectManifestValidator.js.map +1 -0
  61. package/dist/commands/validation/files/ScriptValidator.d.ts +6 -2
  62. package/dist/commands/validation/files/ScriptValidator.js +1 -1
  63. package/dist/commands/validation/files/ScriptValidator.js.map +1 -1
  64. package/dist/commands/validation/validate.js +15 -46
  65. package/dist/commands/validation/validate.js.map +1 -1
  66. package/dist/models/Codenotch.d.ts +0 -205
  67. package/dist/models/Codenotch.js +0 -211
  68. package/dist/models/Codenotch.js.map +1 -1
  69. package/dist/utils/BuildUtils.d.ts +66 -28
  70. package/dist/utils/BuildUtils.js +292 -330
  71. package/dist/utils/BuildUtils.js.map +1 -1
  72. package/dist/utils/ConsoleUtils.d.ts +13 -0
  73. package/dist/utils/ConsoleUtils.js +50 -0
  74. package/dist/utils/ConsoleUtils.js.map +1 -1
  75. package/dist/utils/DbSchemaUtils.d.ts +49 -0
  76. package/dist/utils/DbSchemaUtils.js +246 -0
  77. package/dist/utils/DbSchemaUtils.js.map +1 -0
  78. package/dist/utils/FileUtils.d.ts +104 -15
  79. package/dist/utils/FileUtils.js +285 -100
  80. package/dist/utils/FileUtils.js.map +1 -1
  81. package/dist/utils/GitHubAuthUtils.js.map +1 -1
  82. package/dist/utils/I18nTsUtils.d.ts +66 -0
  83. package/dist/utils/I18nTsUtils.js +221 -0
  84. package/dist/utils/I18nTsUtils.js.map +1 -0
  85. package/dist/utils/ManifestUtils.d.ts +24 -0
  86. package/dist/utils/ManifestUtils.js +53 -0
  87. package/dist/utils/ManifestUtils.js.map +1 -0
  88. package/dist/utils/OpenApiUtils.d.ts +81 -0
  89. package/dist/utils/OpenApiUtils.js +123 -0
  90. package/dist/utils/OpenApiUtils.js.map +1 -0
  91. package/dist/utils/ProjectCompilationUtils.d.ts +3 -30
  92. package/dist/utils/ProjectCompilationUtils.js +26 -760
  93. package/dist/utils/ProjectCompilationUtils.js.map +1 -1
  94. package/dist/utils/ProjectGeneratorUtils.d.ts +35 -0
  95. package/dist/utils/ProjectGeneratorUtils.js +119 -0
  96. package/dist/utils/ProjectGeneratorUtils.js.map +1 -0
  97. package/dist/utils/ProjectTemplates.d.ts +102 -0
  98. package/dist/utils/ProjectTemplates.js +284 -0
  99. package/dist/utils/ProjectTemplates.js.map +1 -0
  100. package/dist/utils/ProjectUtils.d.ts +217 -4
  101. package/dist/utils/ProjectUtils.js +386 -50
  102. package/dist/utils/ProjectUtils.js.map +1 -1
  103. package/dist/utils/RuntimeUtils.js +14 -8
  104. package/dist/utils/RuntimeUtils.js.map +1 -1
  105. package/dist/utils/ScriptDeclarationUtils.d.ts +169 -0
  106. package/dist/utils/ScriptDeclarationUtils.js +398 -0
  107. package/dist/utils/ScriptDeclarationUtils.js.map +1 -0
  108. package/dist/utils/ServeUtils.d.ts +0 -1
  109. package/dist/utils/ServeUtils.js +11 -29
  110. package/dist/utils/ServeUtils.js.map +1 -1
  111. package/dist/utils/ShellUtils.d.ts +6 -0
  112. package/dist/utils/ShellUtils.js +13 -0
  113. package/dist/utils/ShellUtils.js.map +1 -1
  114. package/dist/utils/TemplateUtils.d.ts +62 -0
  115. package/dist/utils/TemplateUtils.js +218 -0
  116. package/dist/utils/TemplateUtils.js.map +1 -0
  117. package/dist/utils/TypeSchemaUtils.d.ts +42 -0
  118. package/dist/utils/TypeSchemaUtils.js +171 -0
  119. package/dist/utils/TypeSchemaUtils.js.map +1 -0
  120. package/package.json +3 -3
  121. package/dist/commands/validation/files/BpmnValidator.js +0 -32
  122. package/dist/commands/validation/files/BpmnValidator.js.map +0 -1
  123. package/dist/commands/validation/files/DbSchemaValidator.d.ts +0 -5
  124. package/dist/commands/validation/files/DbSchemaValidator.js +0 -16
  125. package/dist/commands/validation/files/DbSchemaValidator.js.map +0 -1
  126. package/dist/commands/validation/files/DocumentValidator.d.ts +0 -5
  127. package/dist/commands/validation/files/DocumentValidator.js +0 -12
  128. package/dist/commands/validation/files/DocumentValidator.js.map +0 -1
  129. package/dist/commands/validation/files/ManifestCacheValidator.d.ts +0 -5
  130. package/dist/commands/validation/files/ManifestCacheValidator.js +0 -121
  131. package/dist/commands/validation/files/ManifestCacheValidator.js.map +0 -1
  132. package/dist/commands/validation/files/ManifestValidator.d.ts +0 -5
  133. package/dist/commands/validation/files/ManifestValidator.js +0 -14
  134. package/dist/commands/validation/files/ManifestValidator.js.map +0 -1
  135. package/dist/commands/validation/files/TableValidator.d.ts +0 -5
  136. package/dist/commands/validation/files/TableValidator.js +0 -19
  137. package/dist/commands/validation/files/TableValidator.js.map +0 -1
  138. package/dist/models/DbModels.d.ts +0 -221
  139. package/dist/models/DbModels.js +0 -130
  140. package/dist/models/DbModels.js.map +0 -1
  141. package/dist/models/OpenApiModels.d.ts +0 -115
  142. package/dist/models/OpenApiModels.js +0 -103
  143. package/dist/models/OpenApiModels.js.map +0 -1
  144. package/dist/utils/I18nUtils.d.ts +0 -9
  145. package/dist/utils/I18nUtils.js +0 -54
  146. package/dist/utils/I18nUtils.js.map +0 -1
  147. package/dist/utils/OpenAPIUtils.d.ts +0 -33
  148. package/dist/utils/OpenAPIUtils.js +0 -279
  149. package/dist/utils/OpenAPIUtils.js.map +0 -1
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.NPM_INSTALL_STEP = void 0;
7
+ const TemplateUtils_1 = __importDefault(require("./TemplateUtils"));
8
+ const ManifestUtils_1 = __importDefault(require("./ManifestUtils"));
9
+ const DEFAULT_APP_NAME = 'index';
10
+ /** The install step of the next steps, as 'cn project new' drops it once it ran it */
11
+ exports.NPM_INSTALL_STEP = 'npm install';
12
+ /** Versions pinned by the templates. React 19 is the only supported major */
13
+ const VERSIONS = {
14
+ reactRouter: '^7.6.0',
15
+ tailwind: '^4.1.0',
16
+ tailwindPostcss: '^4.1.0',
17
+ postcss: '^8.5.0'
18
+ };
19
+ /**
20
+ * The starting points 'cn project new' can scaffold.
21
+ *
22
+ * 'default' is the bare project with a routed React app, 'tailwind' adds Tailwind CSS to it, and
23
+ * 'todolist' builds a complete application on top of that: tables, processes, queries and
24
+ * translations. Each of these extends the previous one, so what is fixed in one is fixed in all.
25
+ * 'api' stands apart: a table and its endpoints, without any app nor React.
26
+ *
27
+ * Where the runtime API is not settled yet (`@codenotch/process`, `@codenotch/orm`, `startProcess`),
28
+ * the generated code carries a TODO rather than a guess.
29
+ */
30
+ class ProjectTemplates {
31
+ static TEMPLATES = [
32
+ { name: 'default', description: 'Empty project with a routed React 19 app' },
33
+ { name: 'tailwind', description: 'Same, styled with Tailwind CSS 4 (light and dark themes)' },
34
+ { name: 'todolist', description: 'Complete todo list app: tables, CRUD processes, queries, translations' },
35
+ { name: 'api', description: 'API only: a table and its endpoints, no app at all' }
36
+ ];
37
+ static isTemplateName(value) {
38
+ return this.TEMPLATES.some(template => template.name === value);
39
+ }
40
+ static build(name, context) {
41
+ switch (name) {
42
+ case 'default': return this.buildDefault(context);
43
+ case 'tailwind': return this.buildTailwind(context);
44
+ case 'todolist': return this.buildTodolist(context);
45
+ case 'api': return this.buildApi(context);
46
+ }
47
+ }
48
+ // ------------------------------------------------------------------ default
49
+ static buildDefault(context, styled = false) {
50
+ const manifest = ManifestUtils_1.default.createManifest(context.projectName, { serviceName: context.serviceName });
51
+ manifest.languages = [...context.languages];
52
+ return {
53
+ folders: [...TemplateUtils_1.default.PROJECT_FOLDERS],
54
+ files: [
55
+ { path: 'manifest.json', content: ManifestUtils_1.default.serialize(manifest) },
56
+ { path: 'readme.md', content: TemplateUtils_1.default.createReadmeMd(context.projectName, context.serviceName) },
57
+ { path: '.gitignore', content: TemplateUtils_1.default.createGitignore() },
58
+ {
59
+ path: 'package.json',
60
+ content: this.toJson(TemplateUtils_1.default.createPackageJson(context.serviceName, {
61
+ dependencies: { 'react-router': VERSIONS.reactRouter }
62
+ }))
63
+ },
64
+ { path: `apps/${DEFAULT_APP_NAME}.manifest.json`, content: this.toJson(this.createAppManifest(context.projectName)) },
65
+ { path: `apps/${DEFAULT_APP_NAME}.tsx`, content: this.createRoutedApp(styled) },
66
+ { path: 'apps/components/Layout.tsx', content: this.createLayout(styled) },
67
+ { path: 'apps/pages/HomePage.tsx', content: this.createHomePage(styled) },
68
+ { path: 'apps/pages/AboutPage.tsx', content: this.createAboutPage(styled) },
69
+ { path: 'apps/pages/HelloPage.tsx', content: this.createHelloPage(styled) },
70
+ { path: `apps/${DEFAULT_APP_NAME}.scss`, content: this.createDefaultScss() }
71
+ ],
72
+ nextSteps: [
73
+ exports.NPM_INSTALL_STEP,
74
+ 'cn project start'
75
+ ]
76
+ };
77
+ }
78
+ static createAppManifest(projectName) {
79
+ const manifest = TemplateUtils_1.default.createAppManifest(DEFAULT_APP_NAME);
80
+ // The tab of the browser is better named after the project than after 'index'
81
+ manifest.html = { ...manifest.html, title: projectName };
82
+ return manifest;
83
+ }
84
+ /**
85
+ * The entry of the app: a HashRouter, which keeps the routing client side whatever URL the
86
+ * runtime serves the app from, and one route per page
87
+ */
88
+ static createRoutedApp(styled) {
89
+ return this.lines(`import React from 'react';`, `import { HashRouter, Route, Routes } from 'react-router';`, `import './${DEFAULT_APP_NAME}.scss';`, `import Layout from './components/Layout';`, `import HomePage from './pages/HomePage';`, `import AboutPage from './pages/AboutPage';`, `import HelloPage from './pages/HelloPage';`, ``, `// HashRouter keeps the routing client side, whatever URL the runtime serves the app from`, `const ${DEFAULT_APP_NAME}: React.FC<{}> = () => {`, `\treturn <HashRouter>`, `\t\t<Routes>`, `\t\t\t<Route element={<Layout />}>`, `\t\t\t\t<Route index element={<HomePage />} />`, `\t\t\t\t<Route path="about" element={<AboutPage />} />`, `\t\t\t\t<Route path="hello/:name" element={<HelloPage />} />`, `\t\t\t</Route>`, `\t\t</Routes>`, `\t</HashRouter>;`, `};`, ``, `export default ${DEFAULT_APP_NAME};`, ``);
90
+ }
91
+ static createLayout(styled) {
92
+ const c = this.classes(styled);
93
+ return this.lines(`import React from 'react';`, `import { NavLink, Outlet } from 'react-router';`, `import { useCodenotch } from '@codenotch/codenotch.react';`, ``, `// Shared frame of every page: the navigation, then the page itself in <Outlet />`, `const Layout: React.FC<{}> = () => {`, `\tconst cn = useCodenotch();`, styled ? `\tconst theme = cn.getTheme() ?? 'light';` : undefined, ``, `\treturn <div${c('min-h-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100')}>`, `\t\t<nav${c('flex items-center gap-4 border-b border-gray-200 px-6 py-3 dark:border-gray-700')}>`, `\t\t\t<NavLink to="/"${c('hover:underline')}>Home</NavLink>`, `\t\t\t<NavLink to="/about"${c('hover:underline')}>About</NavLink>`, `\t\t\t<NavLink to="/hello/codenotch"${c('hover:underline')}>Hello</NavLink>`, styled ? `\t\t\t<button${c('ml-auto rounded border border-gray-300 px-3 py-1 text-sm dark:border-gray-600')} onClick={() => cn.setTheme(theme === 'dark' ? 'light' : 'dark')}>` : undefined, styled ? `\t\t\t\t{theme === 'dark' ? 'Light theme' : 'Dark theme'}` : undefined, styled ? `\t\t\t</button>` : undefined, `\t\t</nav>`, `\t\t<main${c('p-6')}>`, `\t\t\t<Outlet />`, `\t\t</main>`, `\t</div>;`, `};`, ``, `export default Layout;`, ``);
94
+ }
95
+ static createHomePage(styled) {
96
+ const c = this.classes(styled);
97
+ return this.lines(`import React from 'react';`, `import { useCodenotch } from '@codenotch/codenotch.react';`, ``, `const HomePage: React.FC<{}> = () => {`, `\tconst cn = useCodenotch();`, ``, `\treturn <div>`, `\t\t<h1${c('text-2xl font-bold')}>My new Codenotch App</h1>`, `\t\t<p${c('mt-2 text-gray-600 dark:text-gray-400')}>The environment the runtime gave this app:</p>`, `\t\t<pre${c('mt-4 overflow-x-auto rounded bg-gray-100 p-4 text-sm dark:bg-gray-800')}>{JSON.stringify(cn.env, null, 4)}</pre>`, `\t</div>;`, `};`, ``, `export default HomePage;`, ``);
98
+ }
99
+ static createAboutPage(styled) {
100
+ const c = this.classes(styled);
101
+ return this.lines(`import React from 'react';`, `import { Link } from 'react-router';`, ``, `const AboutPage: React.FC<{}> = () => {`, `\treturn <div>`, `\t\t<h1${c('text-2xl font-bold')}>About</h1>`, `\t\t<p${c('mt-2')}>This page is reached through the router, without reloading the app.</p>`, `\t\t<Link to="/"${c('mt-4 inline-block underline')}>Back home</Link>`, `\t</div>;`, `};`, ``, `export default AboutPage;`, ``);
102
+ }
103
+ static createHelloPage(styled) {
104
+ const c = this.classes(styled);
105
+ return this.lines(`import React from 'react';`, `import { useParams } from 'react-router';`, ``, `// The ':name' segment of the route is read with useParams()`, `const HelloPage: React.FC<{}> = () => {`, `\tconst { name } = useParams<{ name: string }>();`, ``, `\treturn <div>`, `\t\t<h1${c('text-2xl font-bold')}>Hello {name}!</h1>`, `\t\t<p${c('mt-2')}>Change the name in the URL to greet someone else.</p>`, `\t</div>;`, `};`, ``, `export default HelloPage;`, ``);
106
+ }
107
+ /**
108
+ * The stylesheet of the default app: scss on purpose, so variables, nesting and the rest
109
+ * of the language are ready to use
110
+ */
111
+ static createDefaultScss() {
112
+ return this.lines(`// The stylesheet of the app. Scss is the default here: variables, nesting, functions...`, `$accent: #2563eb;`, `$spacing: 1.5rem;`, ``, `nav {`, ` display: flex;`, ` gap: $spacing;`, ` padding: 0.75rem $spacing;`, ` border-bottom: 1px solid #e5e7eb;`, ``, ` a {`, ` color: inherit;`, ` text-decoration: none;`, ``, ` &:hover {`, ` color: $accent;`, ` text-decoration: underline;`, ` }`, ` }`, `}`, ``, `main {`, ` padding: $spacing;`, ``, ` pre {`, ` overflow-x: auto;`, ` padding: 1rem;`, ` background: #f3f4f6;`, ` border-radius: 0.25rem;`, ` }`, `}`, ``);
113
+ }
114
+ /**
115
+ * The stylesheet of the tailwind flavoured templates: tailwind first, scss for whatever
116
+ * the utilities do not cover
117
+ */
118
+ static createTailwindScss() {
119
+ return this.lines(`@import "tailwindcss";`, ``, `/* useCodenotch().setTheme('dark') toggles the 'dark' class on <html>: key the dark: variants on it */`, `@custom-variant dark (&:where(.dark, .dark *));`, ``, `// Scss on top of tailwind, for what the utilities do not cover`, `$accent: #2563eb;`, ``, `.accent-link {`, ` color: $accent;`, ``, ` &:hover {`, ` text-decoration: underline;`, ` }`, `}`, ``);
120
+ }
121
+ // ------------------------------------------------------------------ tailwind
122
+ static buildTailwind(context) {
123
+ const scaffold = this.buildDefault(context, true);
124
+ this.replaceFile(scaffold, 'package.json', this.toJson(TemplateUtils_1.default.createPackageJson(context.serviceName, {
125
+ dependencies: { 'react-router': VERSIONS.reactRouter },
126
+ devDependencies: {
127
+ 'tailwindcss': VERSIONS.tailwind,
128
+ '@tailwindcss/postcss': VERSIONS.tailwindPostcss,
129
+ 'postcss': VERSIONS.postcss
130
+ }
131
+ })));
132
+ this.replaceFile(scaffold, `apps/${DEFAULT_APP_NAME}.scss`, this.createTailwindScss());
133
+ scaffold.files.push({
134
+ path: 'postcss.config.js',
135
+ // Picked up by the Vite build of 'cn react build', which mirrors the project layout
136
+ content: this.lines(`module.exports = {`, ` plugins: {`, ` '@tailwindcss/postcss': {}`, ` }`, `};`, ``)
137
+ });
138
+ return scaffold;
139
+ }
140
+ // ------------------------------------------------------------------ todolist
141
+ static buildTodolist(context) {
142
+ const scaffold = this.buildTailwind(context);
143
+ scaffold.folders.push('queries');
144
+ // The routed pages of the default app give way to the ones of the todo list
145
+ scaffold.files = scaffold.files.filter(file => !file.path.startsWith('apps/pages/'));
146
+ this.replaceFile(scaffold, `apps/${DEFAULT_APP_NAME}.tsx`, this.createTodolistApp());
147
+ this.replaceFile(scaffold, 'apps/components/Layout.tsx', this.createTodolistLayout(context.languages));
148
+ this.replaceFile(scaffold, 'readme.md', TemplateUtils_1.default.createReadmeMd(context.projectName, context.serviceName) + this.createTodolistReadme());
149
+ scaffold.files.push({ path: 'tables/todolist.ts', content: this.createTodoListTable() }, { path: 'tables/todo.ts', content: this.createTodoTable() }, { path: 'processes/todolists.ts', content: this.createTodoListProcesses() }, { path: 'processes/todos.ts', content: this.createTodoProcesses() }, { path: 'queries/all-todolists.cnql', content: this.createAllTodoListsQuery(context.serviceName) }, { path: 'queries/todos-of-list.cnql', content: this.createTodosOfListQuery(context.serviceName) }, { path: 'globals.i18n.csv', content: this.createTodolistI18n(context.languages) }, { path: 'apps/api.ts', content: this.createTodolistApi() }, { path: 'apps/pages/TodoListsPage.tsx', content: this.createTodoListsPage() }, { path: 'apps/pages/TodoListPage.tsx', content: this.createTodoListPage() });
150
+ scaffold.nextSteps.push(`Search the sources for 'TODO' to see the three points waiting for the runtime API`);
151
+ return scaffold;
152
+ }
153
+ static createTodoListTable() {
154
+ return this.lines(`import { defineEntity, type InferEntity, p } from '@codenotch/orm';`, ``, `export const TodoList = defineEntity({`, ` name: 'TodoList',`, ` properties: {`, ` id: p.uuid().primary(),`, ` name: p.string().notNull(),`, ` // Deleting a list deletes its todos`, ` todos: p.hasMany('Todo').linkBehaviour('Delete'),`, ` },`, `});`, ``, `export type ITodoList = InferEntity<typeof TodoList>;`, ``);
155
+ }
156
+ static createTodoTable() {
157
+ return this.lines(`import { defineEntity, type InferEntity, p } from '@codenotch/orm';`, ``, `export const Todo = defineEntity({`, ` name: 'Todo',`, ` properties: {`, ` id: p.uuid().primary(),`, ` title: p.string().notNull(),`, ` done: p.boolean().default(false),`, ` list: p.belongsTo('TodoList').notNull(),`, ` },`, `});`, ``, `export type ITodo = InferEntity<typeof Todo>;`, ``);
158
+ }
159
+ static createTodoListProcesses() {
160
+ return this.lines(`import { process, api, Context } from '@codenotch/process'`, `import type { ITodoList } from '../tables/todolist'`, ``, `// TODO: 'ctx.tables.<name>' follows the collections of the generated db-schema.ts ('todolists', 'todos').`, `// Adjust the names here if the runtime exposes the tables differently.`, ``, `export const listTodoLists = process({`, ` id: 'ListTodoLists',`, ` description: 'Every todo list, oldest first',`, ` auth: { scope: 'internal' },`, ` trigger: api.get('/todolists'),`, ` run: async (ctx: Context): Promise<ITodoList[]> => {`, ` return ctx.query<ITodoList>('queries/all-todolists.cnql')`, ` },`, `})`, ``, `export const createTodoList = process({`, ` id: 'CreateTodoList',`, ` description: 'Creates an empty todo list',`, ` auth: { scope: 'internal' },`, ` trigger: api.post('/todolists'),`, ` run: async (ctx: Context, input: { name: string }): Promise<{ id: string }> => {`, ` const id = ctx.uuid()`, ` await ctx.tables.todolists.write(id, { name: input.name })`, ` return { id }`, ` },`, `})`, ``, `export const deleteTodoList = process({`, ` id: 'DeleteTodoList',`, ` description: 'Deletes a todo list and, through the relation, its todos',`, ` auth: { scope: 'internal' },`, ` trigger: api.delete('/todolists/:listId'),`, ` run: async (ctx: Context, input: { listId: string }): Promise<{ deleted: boolean }> => {`, ` await ctx.tables.todolists.delete(input.listId)`, ` return { deleted: true }`, ` },`, `})`, ``);
161
+ }
162
+ static createTodoProcesses() {
163
+ return this.lines(`import { process, api, Context } from '@codenotch/process'`, `import type { ITodo } from '../tables/todo'`, ``, `export const listTodos = process({`, ` id: 'ListTodos',`, ` description: 'The todos of a list',`, ` auth: { scope: 'internal' },`, ` trigger: api.get('/todolists/:listId/todos'),`, ` run: async (ctx: Context, input: { listId: string }): Promise<ITodo[]> => {`, ` return ctx.query<ITodo>('queries/todos-of-list.cnql', { listId: input.listId })`, ` },`, `})`, ``, `export const createTodo = process({`, ` id: 'CreateTodo',`, ` description: 'Adds a todo to a list',`, ` auth: { scope: 'internal' },`, ` trigger: api.post('/todolists/:listId/todos'),`, ` run: async (ctx: Context, input: { listId: string; title: string }): Promise<{ id: string }> => {`, ` const id = ctx.uuid()`, ` await ctx.tables.todos.write(id, { title: input.title, done: false, listId: input.listId })`, ` return { id }`, ` },`, `})`, ``, `export const toggleTodo = process({`, ` id: 'ToggleTodo',`, ` description: 'Marks a todo as done, or as to do again',`, ` auth: { scope: 'internal' },`, ` trigger: api.put('/todos/:todoId/toggle'),`, ` run: async (ctx: Context, input: { todoId: string }): Promise<{ done: boolean }> => {`, ` const todo = await ctx.tables.todos.require(input.todoId)`, ` await ctx.tables.todos.write(todo.id, { done: !todo.done })`, ` return { done: !todo.done }`, ` },`, `})`, ``, `export const deleteTodo = process({`, ` id: 'DeleteTodo',`, ` description: 'Removes a todo',`, ` auth: { scope: 'internal' },`, ` trigger: api.delete('/todos/:todoId'),`, ` run: async (ctx: Context, input: { todoId: string }): Promise<{ deleted: boolean }> => {`, ` await ctx.tables.todos.delete(input.todoId)`, ` return { deleted: true }`, ` },`, `})`, ``);
164
+ }
165
+ static createAllTodoListsQuery(serviceName) {
166
+ return this.lines(`<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <TodoList Ref="results">`, ` <Id />`, ` <Name />`, ` <CreatedAt />`, ` </TodoList>`, `</CNQL>`, ``);
167
+ }
168
+ static createTodosOfListQuery(serviceName) {
169
+ return this.lines(`<!-- TODO: filter on the 'listId' parameter passed by the process. The filtering syntax of CNQL is not settled yet. -->`, `<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <Todo Ref="results">`, ` <Id />`, ` <Title />`, ` <Done />`, ` <ListId />`, ` <CreatedAt />`, ` </Todo>`, `</CNQL>`, ``);
170
+ }
171
+ /** One column per language of the manifest, filled for the ones the template knows */
172
+ static createTodolistI18n(languages) {
173
+ const translations = {
174
+ appTitle: { en: 'My todo lists', de: 'Meine Aufgabenlisten', fr: 'Mes listes de tâches' },
175
+ lists: { en: 'Lists', de: 'Listen', fr: 'Listes' },
176
+ newListPlaceholder: { en: 'Name of the new list', de: 'Name der neuen Liste', fr: 'Nom de la nouvelle liste' },
177
+ newTodoPlaceholder: { en: 'What has to be done?', de: 'Was ist zu tun?', fr: 'Que faut-il faire ?' },
178
+ add: { en: 'Add', de: 'Hinzufügen', fr: 'Ajouter' },
179
+ delete: { en: 'Delete', de: 'Löschen', fr: 'Supprimer' },
180
+ backToLists: { en: 'Back to the lists', de: 'Zurück zu den Listen', fr: 'Retour aux listes' },
181
+ noLists: { en: 'No list yet, create the first one.', de: 'Noch keine Liste, erstelle die erste.', fr: 'Aucune liste, créez la première.' },
182
+ noTodos: { en: 'Nothing to do here.', de: 'Hier gibt es nichts zu tun.', fr: 'Rien à faire ici.' },
183
+ todoCount: { en: '{0} todo(s), {1} done', de: '{0} Aufgabe(n), {1} erledigt', fr: '{0} tâche(s), {1} terminée(s)' },
184
+ loading: { en: 'Loading...', de: 'Laden...', fr: 'Chargement...' },
185
+ error: { en: 'Something went wrong: {0}', de: 'Etwas ist schiefgelaufen: {0}', fr: 'Une erreur est survenue : {0}' }
186
+ };
187
+ const quote = (value) => /[",\n]/.test(value) ? `"${value.replaceAll('"', '""')}"` : value;
188
+ const rows = Object.entries(translations).map(([key, values]) =>
189
+ // A language the template does not know gets the english text, to be translated
190
+ [key, ...languages.map(language => quote(values[language] ?? values['en']))].join(','));
191
+ return [['key', ...languages].join(','), ...rows].join('\n') + '\n';
192
+ }
193
+ /**
194
+ * The calls the pages make, in one place. Typed with the row types of the tables, so the UI
195
+ * and the processes cannot drift apart silently.
196
+ */
197
+ static createTodolistApi() {
198
+ return this.lines(`import { getCodenotch } from '@codenotch/codenotch.react';`, `import type { ITodoList } from '../tables/todolist';`, `import type { ITodo } from '../tables/todo';`, ``, `// TODO: startProcess() is not implemented yet in @codenotch/codenotch.react 2.0.1 (it throws 'Not implemented.').`, `// The calls below are typed against the processes of ./processes and will work as is once it lands;`, `// until then the pages show the error it raises.`, `async function call<TInput, TOutput>(processId: string, input: TInput): Promise<TOutput> {`, `\tconst result = await getCodenotch().startProcess<string, TInput, TOutput>(processId, input);`, `\tif (result.isError) {`, `\t\tthrow new Error(result.errorMessage);`, `\t}`, `\treturn result.output;`, `}`, ``, `export const listTodoLists = () => call<{}, ITodoList[]>('ListTodoLists', {});`, `export const createTodoList = (name: string) => call<{ name: string }, { id: string }>('CreateTodoList', { name });`, `export const deleteTodoList = (listId: string) => call<{ listId: string }, { deleted: boolean }>('DeleteTodoList', { listId });`, ``, `export const listTodos = (listId: string) => call<{ listId: string }, ITodo[]>('ListTodos', { listId });`, `export const createTodo = (listId: string, title: string) => call<{ listId: string; title: string }, { id: string }>('CreateTodo', { listId, title });`, `export const toggleTodo = (todoId: string) => call<{ todoId: string }, { done: boolean }>('ToggleTodo', { todoId });`, `export const deleteTodo = (todoId: string) => call<{ todoId: string }, { deleted: boolean }>('DeleteTodo', { todoId });`, ``);
199
+ }
200
+ static createTodolistApp() {
201
+ return this.lines(`import React from 'react';`, `import { HashRouter, Route, Routes } from 'react-router';`, `import './${DEFAULT_APP_NAME}.scss';`, `import Layout from './components/Layout';`, `import TodoListsPage from './pages/TodoListsPage';`, `import TodoListPage from './pages/TodoListPage';`, ``, `// HashRouter keeps the routing client side, whatever URL the runtime serves the app from`, `const ${DEFAULT_APP_NAME}: React.FC<{}> = () => {`, `\treturn <HashRouter>`, `\t\t<Routes>`, `\t\t\t<Route element={<Layout />}>`, `\t\t\t\t<Route index element={<TodoListsPage />} />`, `\t\t\t\t<Route path="lists/:listId" element={<TodoListPage />} />`, `\t\t\t</Route>`, `\t\t</Routes>`, `\t</HashRouter>;`, `};`, ``, `export default ${DEFAULT_APP_NAME};`, ``);
202
+ }
203
+ static createTodolistLayout(languages) {
204
+ return this.lines(`import React from 'react';`, `import { Link, Outlet } from 'react-router';`, `import { useCodenotch } from '@codenotch/codenotch.react';`, `import globals from '../../globals.i18n';`, ``, `// Shared frame of every page: title, language and theme switches, then the page itself in <Outlet />`, `const Layout: React.FC<{}> = () => {`, `\tconst cn = useCodenotch();`, `\tconst theme = cn.getTheme() ?? 'light';`, `\tconst language = cn.getLanguage() ?? '${languages[0] ?? 'en'}';`, ``, `\t// The generated translation class follows the language of the runtime`, `\tglobals.setLanguage(language);`, ``, `\treturn <div className="min-h-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">`, `\t\t<nav className="flex items-center gap-4 border-b border-gray-200 px-6 py-3 dark:border-gray-700">`, `\t\t\t<Link to="/" className="text-lg font-semibold">{globals.appTitle()}</Link>`, `\t\t\t<div className="ml-auto flex items-center gap-2 text-sm">`, `\t\t\t\t{cn.getLanguages().map(lang =>`, `\t\t\t\t\t<button key={lang} onClick={() => cn.setLanguage(lang)}`, `\t\t\t\t\t\tclassName={'rounded px-2 py-1 ' + (lang === language ? 'bg-gray-200 dark:bg-gray-700' : 'hover:underline')}>`, `\t\t\t\t\t\t{lang.toUpperCase()}`, `\t\t\t\t\t</button>)}`, `\t\t\t\t<button className="rounded border border-gray-300 px-3 py-1 dark:border-gray-600" onClick={() => cn.setTheme(theme === 'dark' ? 'light' : 'dark')}>`, `\t\t\t\t\t{theme === 'dark' ? 'Light' : 'Dark'}`, `\t\t\t\t</button>`, `\t\t\t</div>`, `\t\t</nav>`, `\t\t<main className="mx-auto max-w-2xl p-6">`, `\t\t\t<Outlet />`, `\t\t</main>`, `\t</div>;`, `};`, ``, `export default Layout;`, ``);
205
+ }
206
+ static createTodoListsPage() {
207
+ return this.lines(`import React, { useEffect, useState } from 'react';`, `import { Link } from 'react-router';`, `import type { ITodoList } from '../../tables/todolist';`, `import globals from '../../globals.i18n';`, `import { createTodoList, deleteTodoList, listTodoLists } from '../api';`, ``, `const TodoListsPage: React.FC<{}> = () => {`, `\tconst [lists, setLists] = useState<ITodoList[] | undefined>(undefined);`, `\tconst [name, setName] = useState('');`, `\tconst [error, setError] = useState<string | undefined>(undefined);`, ``, `\tconst refresh = () => listTodoLists().then(setLists).catch(e => setError(String(e)));`, ``, `\tuseEffect(() => { refresh(); }, []);`, ``, `\tconst add = async (event: React.FormEvent) => {`, `\t\tevent.preventDefault();`, `\t\tif (name.trim() === '') {`, `\t\t\treturn;`, `\t\t}`, `\t\ttry {`, `\t\t\tawait createTodoList(name.trim());`, `\t\t\tsetName('');`, `\t\t\tawait refresh();`, `\t\t}`, `\t\tcatch (e) {`, `\t\t\tsetError(String(e));`, `\t\t}`, `\t};`, ``, `\tconst remove = async (listId: string) => {`, `\t\ttry {`, `\t\t\tawait deleteTodoList(listId);`, `\t\t\tawait refresh();`, `\t\t}`, `\t\tcatch (e) {`, `\t\t\tsetError(String(e));`, `\t\t}`, `\t};`, ``, `\treturn <div>`, `\t\t<h1 className="text-2xl font-bold">{globals.lists()}</h1>`, ``, `\t\t<form onSubmit={add} className="mt-4 flex gap-2">`, `\t\t\t<input value={name} onChange={e => setName(e.target.value)} placeholder={globals.newListPlaceholder()}`, `\t\t\t\tclassName="flex-1 rounded border border-gray-300 px-3 py-2 dark:border-gray-600 dark:bg-gray-800" />`, `\t\t\t<button type="submit" className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700">{globals.add()}</button>`, `\t\t</form>`, ``, `\t\t{error && <p className="mt-4 rounded bg-red-100 p-3 text-red-800 dark:bg-red-900 dark:text-red-200">{globals.error(error)}</p>}`, `\t\t{lists === undefined && !error && <p className="mt-4 text-gray-500">{globals.loading()}</p>}`, `\t\t{lists !== undefined && lists.length === 0 && <p className="mt-4 text-gray-500">{globals.noLists()}</p>}`, ``, `\t\t<ul className="mt-4 divide-y divide-gray-200 dark:divide-gray-700">`, `\t\t\t{lists?.map(list =>`, `\t\t\t\t<li key={list.id} className="flex items-center justify-between py-3">`, `\t\t\t\t\t<Link to={'/lists/' + list.id} className="hover:underline">{list.name}</Link>`, `\t\t\t\t\t<button onClick={() => remove(list.id)} className="text-sm text-red-600 hover:underline">{globals.delete()}</button>`, `\t\t\t\t</li>)}`, `\t\t</ul>`, `\t</div>;`, `};`, ``, `export default TodoListsPage;`, ``);
208
+ }
209
+ static createTodoListPage() {
210
+ return this.lines(`import React, { useEffect, useState } from 'react';`, `import { Link, useParams } from 'react-router';`, `import type { ITodo } from '../../tables/todo';`, `import globals from '../../globals.i18n';`, `import { createTodo, deleteTodo, listTodos, toggleTodo } from '../api';`, ``, `const TodoListPage: React.FC<{}> = () => {`, `\tconst { listId } = useParams<{ listId: string }>();`, `\tconst [todos, setTodos] = useState<ITodo[] | undefined>(undefined);`, `\tconst [title, setTitle] = useState('');`, `\tconst [error, setError] = useState<string | undefined>(undefined);`, ``, `\tconst refresh = () => listTodos(listId!).then(setTodos).catch(e => setError(String(e)));`, ``, `\tuseEffect(() => { refresh(); }, [listId]);`, ``, `\t// Every change goes through a process, then the list is read again: the server is the truth`, `\tconst run = async (action: () => Promise<unknown>) => {`, `\t\ttry {`, `\t\t\tawait action();`, `\t\t\tawait refresh();`, `\t\t}`, `\t\tcatch (e) {`, `\t\t\tsetError(String(e));`, `\t\t}`, `\t};`, ``, `\tconst add = (event: React.FormEvent) => {`, `\t\tevent.preventDefault();`, `\t\tif (title.trim() !== '') {`, `\t\t\trun(() => createTodo(listId!, title.trim())).then(() => setTitle(''));`, `\t\t}`, `\t};`, ``, `\tconst done = todos?.filter(todo => todo.done).length ?? 0;`, ``, `\treturn <div>`, `\t\t<Link to="/" className="text-sm underline">{globals.backToLists()}</Link>`, `\t\t<h1 className="mt-2 text-2xl font-bold">{globals.appTitle()}</h1>`, `\t\t{todos !== undefined && <p className="text-sm text-gray-500">{globals.todoCount(String(todos.length), String(done))}</p>}`, ``, `\t\t<form onSubmit={add} className="mt-4 flex gap-2">`, `\t\t\t<input value={title} onChange={e => setTitle(e.target.value)} placeholder={globals.newTodoPlaceholder()}`, `\t\t\t\tclassName="flex-1 rounded border border-gray-300 px-3 py-2 dark:border-gray-600 dark:bg-gray-800" />`, `\t\t\t<button type="submit" className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700">{globals.add()}</button>`, `\t\t</form>`, ``, `\t\t{error && <p className="mt-4 rounded bg-red-100 p-3 text-red-800 dark:bg-red-900 dark:text-red-200">{globals.error(error)}</p>}`, `\t\t{todos === undefined && !error && <p className="mt-4 text-gray-500">{globals.loading()}</p>}`, `\t\t{todos !== undefined && todos.length === 0 && <p className="mt-4 text-gray-500">{globals.noTodos()}</p>}`, ``, `\t\t<ul className="mt-4 divide-y divide-gray-200 dark:divide-gray-700">`, `\t\t\t{todos?.map(todo =>`, `\t\t\t\t<li key={todo.id} className="flex items-center gap-3 py-3">`, `\t\t\t\t\t<input type="checkbox" checked={todo.done} onChange={() => run(() => toggleTodo(todo.id))} />`, `\t\t\t\t\t<span className={'flex-1 ' + (todo.done ? 'text-gray-400 line-through' : '')}>{todo.title}</span>`, `\t\t\t\t\t<button onClick={() => run(() => deleteTodo(todo.id))} className="text-sm text-red-600 hover:underline">{globals.delete()}</button>`, `\t\t\t\t</li>)}`, `\t\t</ul>`, `\t</div>;`, `};`, ``, `export default TodoListPage;`, ``);
211
+ }
212
+ static createTodolistReadme() {
213
+ return this.lines(``, `## The todo list template`, ``, `| Path | Role |`, `| --- | --- |`, `| \`tables/todolist.ts\`, \`tables/todo.ts\` | the two tables, a list having many todos |`, `| \`processes/todolists.ts\`, \`processes/todos.ts\` | one process per operation, each exposed as an API endpoint |`, `| \`queries/*.cnql\` | the read queries the processes run |`, `| \`globals.i18n.csv\` | the texts of the app in every language of the manifest |`, `| \`apps/api.ts\` | the typed calls the pages make to the processes |`, `| \`apps/pages/\` | the list of lists, and the todos of one list |`, `| \`db-schema.xml\`, \`db-schema.ts\`, \`globals.i18n.ts\`, \`openapi.json\` | generated from the files above, never edit them |`, ``, `After a change to the tables, the translations or the processes, regenerate the derived files:`, ``, '```sh', `cn generate dbcontext -f .`, `cn generate i18n -f globals.i18n.csv`, `cn generate openapi -f .`, '```', ``, `Three points wait for the runtime API and are marked \`TODO\` in the sources: the client call of a`, `process (\`apps/api.ts\`), the filter of a query (\`queries/todos-of-list.cnql\`) and the names the`, `tables are reached under from a process (\`processes/*.ts\`).`, ``);
214
+ }
215
+ // ------------------------------------------------------------------ api
216
+ /**
217
+ * A project exposing an API and nothing else: no app, no React, no translations.
218
+ * The product catalog it manages is small on purpose, every kind of endpoint is there once.
219
+ */
220
+ static buildApi(context) {
221
+ const manifest = ManifestUtils_1.default.createManifest(context.projectName, { serviceName: context.serviceName });
222
+ manifest.languages = [...context.languages];
223
+ return {
224
+ folders: [...TemplateUtils_1.default.PROJECT_FOLDERS.filter(folder => folder !== 'apps'), 'queries'],
225
+ files: [
226
+ { path: 'manifest.json', content: ManifestUtils_1.default.serialize(manifest) },
227
+ { path: 'readme.md', content: TemplateUtils_1.default.createReadmeMd(context.projectName, context.serviceName) + this.createApiReadme() },
228
+ { path: '.gitignore', content: TemplateUtils_1.default.createGitignore() },
229
+ { path: 'package.json', content: this.toJson(TemplateUtils_1.default.createPackageJson(context.serviceName, {}, false)) },
230
+ { path: 'tables/product.ts', content: this.createProductTable() },
231
+ { path: 'processes/products.ts', content: this.createProductProcesses() },
232
+ { path: 'processes/maintenance.ts', content: this.createMaintenanceProcesses() },
233
+ { path: 'queries/all-products.cnql', content: this.createAllProductsQuery(context.serviceName) },
234
+ { path: 'queries/low-stock.cnql', content: this.createLowStockQuery(context.serviceName) }
235
+ ],
236
+ nextSteps: [
237
+ exports.NPM_INSTALL_STEP,
238
+ 'cn project start',
239
+ `Search the sources for 'TODO' to see the points waiting for the runtime API`
240
+ ]
241
+ };
242
+ }
243
+ static createProductTable() {
244
+ return this.lines(`import { defineEntity, type InferEntity, p } from '@codenotch/orm';`, ``, `export const Product = defineEntity({`, ` name: 'Product',`, ` properties: {`, ` id: p.uuid().primary(),`, ` sku: p.string().notNull().unique(),`, ` name: p.string().notNull(),`, ` price: p.decimal().notNull(),`, ` stock: p.integer().default(0),`, ` active: p.boolean().default(true),`, ` },`, `});`, ``, `export type IProduct = InferEntity<typeof Product>;`, ``);
245
+ }
246
+ /** One process per operation of the catalog, each exposed as an endpoint */
247
+ static createProductProcesses() {
248
+ return this.lines(`import { process, api, Context } from '@codenotch/process'`, `import type { IProduct } from '../tables/product'`, ``, `// TODO: 'ctx.tables.<name>' follows the collections of the generated db-schema.ts ('products').`, `// Adjust the name here if the runtime exposes the table differently.`, ``, `export const listProducts = process({`, ` id: 'ListProducts',`, ` description: 'The products of the catalog, optionally the active ones only',`, ` auth: { scope: 'public' },`, ` trigger: api.get('/products'),`, ` run: async (ctx: Context, input: { activeOnly?: boolean }): Promise<IProduct[]> => {`, ` return ctx.query<IProduct>('queries/all-products.cnql', { activeOnly: input.activeOnly ?? false })`, ` },`, `})`, ``, `export const getProduct = process({`, ` id: 'GetProduct',`, ` description: 'One product by id, 404 when it does not exist',`, ` auth: { scope: 'public' },`, ` trigger: api.get('/products/:productId'),`, ` run: async (ctx: Context, input: { productId: string }): Promise<IProduct> => {`, ` // require() throws when the id is unknown, which an api process turns into a 404`, ` return ctx.tables.products.require(input.productId)`, ` },`, `})`, ``, `export const createProduct = process({`, ` id: 'CreateProduct',`, ` description: 'Adds a product to the catalog',`, ` auth: { scope: 'internal' },`, ` trigger: api.post('/products'),`, ` run: async (ctx: Context, input: { sku: string; name: string; price: number; stock?: number }): Promise<{ id: string }> => {`, ` const id = ctx.uuid()`, ` await ctx.tables.products.write(id, { sku: input.sku, name: input.name, price: input.price, stock: input.stock ?? 0, active: true })`, ` return { id }`, ` },`, `})`, ``, `export const updateProduct = process({`, ` id: 'UpdateProduct',`, ` description: 'Changes the given fields of a product, the others are left as they are',`, ` auth: { scope: 'internal' },`, ` trigger: api.put('/products/:productId'),`, ` run: async (ctx: Context, input: { productId: string; name?: string; price?: number; stock?: number; active?: boolean }): Promise<{ updated: boolean }> => {`, ` const { productId, ...changes } = input`, ` // Only the fields actually sent are written, an absent one must not erase the current value`, ` const patch = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined))`, ` await ctx.tables.products.require(productId)`, ` await ctx.tables.products.write(productId, patch)`, ` return { updated: true }`, ` },`, `})`, ``, `export const deleteProduct = process({`, ` id: 'DeleteProduct',`, ` description: 'Removes a product from the catalog',`, ` auth: { scope: 'internal' },`, ` trigger: api.delete('/products/:productId'),`, ` run: async (ctx: Context, input: { productId: string }): Promise<{ deleted: boolean }> => {`, ` await ctx.tables.products.delete(input.productId)`, ` return { deleted: true }`, ` },`, `})`, ``);
249
+ }
250
+ /** What an API project runs on its own: a health check and a scheduled report */
251
+ static createMaintenanceProcesses() {
252
+ return this.lines(`import { process, api, timer, Context } from '@codenotch/process'`, `import type { IProduct } from '../tables/product'`, ``, `export const health = process({`, ` id: 'Health',`, ` description: 'Tells the monitoring the service is up',`, ` auth: { scope: 'public' },`, ` trigger: api.get('/health'),`, ` run: async (): Promise<{ status: 'ok' }> => ({ status: 'ok' }),`, `})`, ``, `// No endpoint here: the runtime starts this one every morning at 7`, `export const lowStockReport = process({`, ` id: 'LowStockReport',`, ` description: 'Logs the products running out of stock',`, ` auth: { scope: 'system' },`, ` trigger: timer('0 7 * * *'),`, ` run: async (ctx: Context): Promise<{ lowStock: number }> => {`, ` const products = await ctx.query<IProduct>('queries/low-stock.cnql', { threshold: 5 })`, ` for (const product of products) {`, ` ctx.log.info('low stock', { sku: product.sku, stock: product.stock })`, ` }`, ` return { lowStock: products.length }`, ` },`, `})`, ``);
253
+ }
254
+ static createAllProductsQuery(serviceName) {
255
+ return this.lines(`<!-- TODO: filter on 'activeOnly' when it is true. The filtering syntax of CNQL is not settled yet. -->`, `<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <Product Ref="results">`, ` <Id />`, ` <Sku />`, ` <Name />`, ` <Price />`, ` <Stock />`, ` <Active />`, ` </Product>`, `</CNQL>`, ``);
256
+ }
257
+ static createLowStockQuery(serviceName) {
258
+ return this.lines(`<!-- TODO: keep the products whose Stock is below the 'threshold' parameter. The filtering syntax of CNQL is not settled yet. -->`, `<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <Product Ref="results">`, ` <Id />`, ` <Sku />`, ` <Stock />`, ` </Product>`, `</CNQL>`, ``);
259
+ }
260
+ static createApiReadme() {
261
+ return this.lines(``, `## The API template`, ``, `This project has no app: it exposes the endpoints of \`openapi.json\` and nothing else, so`, `\`package.json\` carries no React at all.`, ``, `| Path | Role |`, `| --- | --- |`, `| \`tables/product.ts\` | the catalog table |`, `| \`processes/products.ts\` | one process per operation, each exposed as an endpoint |`, `| \`processes/maintenance.ts\` | the health check, and a report the runtime schedules every morning |`, `| \`queries/*.cnql\` | the read queries the processes run |`, `| \`db-schema.xml\`, \`db-schema.ts\`, \`openapi.json\` | generated from the files above, never edit them |`, ``, `After a change to the table or the processes, regenerate the derived files:`, ``, '```sh', `cn generate dbcontext -f .`, `cn generate openapi -f .`, '```', ``, `Two points wait for the runtime API and are marked \`TODO\` in the sources: the filters of the`, `queries (\`queries/*.cnql\`) and the name the table is reached under from a process (\`processes/*.ts\`).`, ``);
262
+ }
263
+ // ------------------------------------------------------------------ helpers
264
+ /** Joins lines, dropping the undefined ones so that optional lines read naturally */
265
+ static lines(...lines) {
266
+ return lines.filter((line) => line !== undefined).join('\n');
267
+ }
268
+ /** ` className="..."` when the template is styled, nothing otherwise */
269
+ static classes(styled) {
270
+ return classNames => styled ? ` className="${classNames}"` : '';
271
+ }
272
+ static replaceFile(scaffold, path, content) {
273
+ const file = scaffold.files.find(candidate => candidate.path === path);
274
+ if (file === undefined) {
275
+ throw new Error(`Template file '${path}' not found`);
276
+ }
277
+ file.content = content;
278
+ }
279
+ static toJson(value) {
280
+ return JSON.stringify(value, null, 4) + '\n';
281
+ }
282
+ }
283
+ exports.default = ProjectTemplates;
284
+ //# sourceMappingURL=ProjectTemplates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectTemplates.js","sourceRoot":"","sources":["../../src/utils/ProjectTemplates.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,140 @@
1
- import { IScenarioProjectManifest } from '../models/Codenotch';
2
1
  import { CompilationResult } from './ProjectCompilationUtils';
2
+ import { LiteralValue } from './ScriptDeclarationUtils';
3
+ import { IJsonSchema } from './TypeSchemaUtils';
4
+ import { IAppManifest, ICodenotchProjectManifest } from '@codenotch/codenotch.core';
5
+ /** A *.i18n.csv file found in a project */
6
+ export interface IDiscoveredI18n {
7
+ /** Absolute path of the file */
8
+ filePath: string;
9
+ /** Translation keys it declares, in file order. Empty when its header has no 'key' column */
10
+ keys: string[];
11
+ /** Languages of its columns, e.g. ['en', 'de'] */
12
+ languages: string[];
13
+ }
14
+ /** A call applied to the type of a column, e.g. `.default('')` or `.primary()` */
15
+ export interface IColumnModifier {
16
+ name: string;
17
+ arguments: LiteralValue[];
18
+ }
19
+ /** A column of a table, read from `fullName: p.string()` */
20
+ export interface ITableColumn {
21
+ /** Name of the column, e.g. 'fullName' */
22
+ name: string;
23
+ /** Type it is built with, e.g. 'string', 'integer', 'text' */
24
+ type?: string;
25
+ /** Arguments given to the type itself, e.g. the 255 of p.string(255) */
26
+ typeArguments: LiteralValue[];
27
+ /** True when '.primary()' is applied */
28
+ isPrimary: boolean;
29
+ /** Value given to '.default(...)' when it is a literal */
30
+ defaultValue?: LiteralValue;
31
+ /** Every call applied after the type, in source order */
32
+ modifiers: IColumnModifier[];
33
+ }
34
+ /** A `defineEntity({ ... })` declaration found in a project */
35
+ export interface IDiscoveredTable {
36
+ /** Absolute path of the script declaring it */
37
+ filePath: string;
38
+ /** Value of its 'name' property, e.g. 'User' */
39
+ name?: string;
40
+ /** False when the table has a name that is built at runtime, so it cannot be known from the sources */
41
+ isNameStatic: boolean;
42
+ /** Name of the variable it is assigned to, e.g. 'User' */
43
+ variableName?: string;
44
+ /** True when that variable is exported */
45
+ isExported: boolean;
46
+ /** 1-based line of the declaration, to point at it in a diagnostic */
47
+ line: number;
48
+ /** Columns of the table, in source order */
49
+ columns: ITableColumn[];
50
+ /** Name of its exported row type, e.g. 'IUser' from `export type IUser = InferEntity<typeof User>` */
51
+ typeName?: string;
52
+ /**
53
+ * Line to paste to use that type, with a path relative to the root of the project, e.g.
54
+ * "import { IUser } from './tables/user';". Use {@link ProjectUtils.getTableTypeImport}
55
+ * to get it relative to another file.
56
+ */
57
+ importStatement?: string;
58
+ /** Every property written literally, for whatever is not covered by the fields above */
59
+ properties: Record<string, LiteralValue>;
60
+ }
61
+ /** A field of a document. Same shape as a table column, since both are declared the same way */
62
+ export type IDocumentField = ITableColumn;
63
+ /** A `defineDocument({ ... })` declaration found in a project */
64
+ export interface IDiscoveredDocument extends Omit<IDiscoveredTable, 'columns'> {
65
+ /** Fields of the document, in source order */
66
+ fields: IDocumentField[];
67
+ }
68
+ /** A React app found in a project: a tsx/jsx script and its sibling manifest */
69
+ export interface IDiscoveredApp {
70
+ /** Absolute path of the '<App>.manifest.json' */
71
+ manifestPath: string;
72
+ /** Absolute path of the '<App>.tsx' or '<App>.jsx' */
73
+ scriptPath: string;
74
+ /** Syntax of the script */
75
+ type: 'jsx' | 'tsx';
76
+ /** Name shared by both files, e.g. 'Dashboard' for 'Dashboard.tsx' */
77
+ name: string;
78
+ /** Content of the manifest, empty when it cannot be parsed */
79
+ manifest: IAppManifest;
80
+ }
81
+ /** How a process is started, read from its `trigger` property */
82
+ export type IProcessTrigger =
83
+ /** trigger: api.post('/books') */
84
+ {
85
+ kind: 'api';
86
+ method: string;
87
+ route: string;
88
+ }
89
+ /** trigger: timer('0 8 * * *') */
90
+ | {
91
+ kind: 'timer';
92
+ cron: string;
93
+ }
94
+ /** trigger: signal('book.returned') */
95
+ | {
96
+ kind: 'signal';
97
+ ref: string;
98
+ }
99
+ /** trigger: install({ once: true }) */
100
+ | {
101
+ kind: 'install';
102
+ options: Record<string, LiteralValue>;
103
+ }
104
+ /** A trigger that is not one of the known factories, or that is built at runtime */
105
+ | {
106
+ kind: 'unknown';
107
+ callee: string;
108
+ };
109
+ /** A `process({ ... })` declaration found in a project */
110
+ export interface IDiscoveredProcess {
111
+ /** Absolute path of the script declaring it */
112
+ filePath: string;
113
+ /** Value of its 'id', undefined when absent or computed (see isIdStatic) */
114
+ id?: string;
115
+ /** False when the process has an id that is built at runtime, so it cannot be known from the sources */
116
+ isIdStatic: boolean;
117
+ /** Name of the variable it is assigned to, e.g. 'createBook' */
118
+ variableName?: string;
119
+ /** True when that variable is exported, which a process needs to be reachable */
120
+ isExported: boolean;
121
+ /** 1-based line of the declaration, to point at it in a diagnostic */
122
+ line: number;
123
+ /** Value of 'auth.scope', e.g. 'internal' or 'system' */
124
+ scope?: string;
125
+ /** How it is started, undefined when it has no trigger and is only reachable through ctx.call */
126
+ trigger?: IProcessTrigger;
127
+ /** True when 'run' declares a second parameter, whether or not it is typed */
128
+ takesInput: boolean;
129
+ /** Schema of the second parameter of 'run' (its input), when it carries a type annotation */
130
+ input?: IJsonSchema;
131
+ /** Schema of the declared return type of 'run', `Promise<T>` unwrapped, when it carries one */
132
+ output?: IJsonSchema;
133
+ /** Literal 'description' of the declaration, when any */
134
+ description?: string;
135
+ /** Every property written literally, for whatever is not covered by the fields above */
136
+ properties: Record<string, LiteralValue>;
137
+ }
3
138
  export default abstract class ProjectUtils {
4
139
  /**
5
140
  * Compile and package the project to prepare it for deployment
@@ -7,9 +142,87 @@ export default abstract class ProjectUtils {
7
142
  * Output a zip file that can be deployed to Codenotch
8
143
  */
9
144
  static packageProject(sourcePath: string, outputPath: string, ignoreErrors?: boolean): Promise<CompilationResult>;
10
- static isManifestCompiled(projectPath: string): Promise<boolean>;
11
145
  private static getCollectionCount;
12
- static getManifest(projectPath: string): Promise<IScenarioProjectManifest>;
146
+ static getManifest(projectPath: string): Promise<ICodenotchProjectManifest>;
13
147
  static getProjectServiceName(projectPath: string): Promise<string>;
14
- static inspectProject(projectPath: string): Promise<void>;
148
+ static inspectProject(projectPath: string): Promise<{
149
+ manifest: ICodenotchProjectManifest | undefined;
150
+ processes: IDiscoveredProcess[];
151
+ tables: IDiscoveredTable[];
152
+ documents: IDiscoveredDocument[];
153
+ apps: IDiscoveredApp[];
154
+ i18ns: IDiscoveredI18n[];
155
+ pkgJson: {
156
+ name?: string;
157
+ version?: string;
158
+ } | undefined;
159
+ }>;
160
+ /** Short human readable form of a process trigger, e.g. 'POST /books' or 'cron 0 8 * * *' */
161
+ private static describeTrigger;
162
+ /**
163
+ * Find all file (recursivly) in a given folder (workspacePath) that contains a table declaration ('defineEntity').
164
+ */
165
+ static discoverTableFiles(workspacePath: string): Promise<IDiscoveredTable[]>;
166
+ /**
167
+ * Shared by the tables and the documents, which only differ by the factory they are declared with
168
+ *
169
+ * @param factoryName 'defineEntity' or 'defineDocument'
170
+ * @param typeHelperNames helpers building their row type, e.g. ['InferEntity']
171
+ */
172
+ private static discoverOrmDeclarations;
173
+ /**
174
+ * Line to paste to use the type of a table from another file, e.g.
175
+ * "import { IUser } from '../tables/user';"
176
+ *
177
+ * @param table table to import the type of
178
+ * @param fromFilePath file the import would be written in
179
+ */
180
+ static getTableTypeImport(table: IDiscoveredTable, fromFilePath: string): string | undefined;
181
+ private static buildTypeImport;
182
+ /**
183
+ * Turns the `properties` of a table into columns: `bio: p.text().default('')` gives the
184
+ * type 'text' and the modifier 'default' with its value.
185
+ */
186
+ private static readColumns;
187
+ /**
188
+ * Find all file (recursivly) in a given folder (workspacePath) that contains a document declaration ('defineDocument').
189
+ */
190
+ static discoverDocumentFiles(workspacePath: string): Promise<IDiscoveredDocument[]>;
191
+ /**
192
+ * Line to paste to use the type of a document from another file, e.g.
193
+ * "import { ISettings } from '../documents/settings';"
194
+ *
195
+ * @param document document to import the type of
196
+ * @param fromFilePath file the import would be written in
197
+ */
198
+ static getDocumentTypeImport(document: IDiscoveredDocument, fromFilePath: string): string | undefined;
199
+ /**
200
+ * Find all file (recursivly) in a given folder (workspacePath) that contains a table declaration ('process').
201
+ */
202
+ static discoverProcessFiles(workspacePath: string): Promise<IDiscoveredProcess[]>;
203
+ /** Value of `auth: { scope: 'internal' }` when it is written literally */
204
+ private static readScope;
205
+ /**
206
+ * Turns the `trigger` property into something usable, e.g. `api.post('/books')` into
207
+ * { kind: 'api', method: 'POST', route: '/books' }.
208
+ * A process without trigger is only reachable through ctx.call, and returns undefined.
209
+ */
210
+ private static readTrigger;
211
+ private static getAllScriptFile;
212
+ /**
213
+ * An app is a tsx/jsx file with a sibling *.manifest.json.
214
+ *
215
+ * A script without its manifest is not an app and is left out, whereas an app whose manifest
216
+ * cannot be parsed is still reported, with an empty manifest: it exists on disk, and saying
217
+ * why its manifest is wrong is the job of the validators.
218
+ */
219
+ static discoverAppsFiles(workspacePath: string): Promise<IDiscoveredApp[]>;
220
+ /**
221
+ * Find all *.i18n.csv files (recursively) in a given folder (workspacePath), with the keys
222
+ * they declare and the languages they translate them into.
223
+ *
224
+ * A file whose first column is not 'key' is reported with no key and no language rather than
225
+ * being read wrong: saying why it is malformed is the job of the validators.
226
+ */
227
+ static discoverI18nsFiles(workspacePath: string): Promise<IDiscoveredI18n[]>;
15
228
  }