odoro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +12 -0
- package/client.d.ts +84 -0
- package/dist/build-SNTGJH2J.js +4 -0
- package/dist/chunk-2YDI5NKV.js +55 -0
- package/dist/chunk-G2WW7N4C.js +1872 -0
- package/dist/chunk-G5QXVBYT.js +1139 -0
- package/dist/chunk-JMEHF3KN.js +35 -0
- package/dist/chunk-PEMYUK2D.js +64 -0
- package/dist/chunk-T42X2NJN.js +98 -0
- package/dist/chunk-T6RLHSCW.js +123 -0
- package/dist/chunk-UGRSODHU.js +154 -0
- package/dist/cli.d.ts +40 -0
- package/dist/cli.js +236 -0
- package/dist/commands-FVAHVVVN.js +793 -0
- package/dist/commands-V44Y5G4F.js +245 -0
- package/dist/create-SH2M722Y.js +288 -0
- package/dist/index.d.ts +354 -0
- package/dist/index.js +7 -0
- package/dist/package-AUINPBEX.js +62 -0
- package/dist/preview-LOAO5Y6V.js +3 -0
- package/dist/registry/index.d.ts +316 -0
- package/dist/registry/index.js +2 -0
- package/dist/server-MZ76LPAG.js +3 -0
- package/package.json +57 -0
- package/templates/react-ts/README.md +52 -0
- package/templates/react-ts/_gitignore +8 -0
- package/templates/react-ts/index.html +13 -0
- package/templates/react-ts/odoro.config.ts +10 -0
- package/templates/react-ts/package.json +23 -0
- package/templates/react-ts/public/favicon.svg +4 -0
- package/templates/react-ts/src/App.tsx +66 -0
- package/templates/react-ts/src/main.tsx +18 -0
- package/templates/react-ts/src/odoro-env.d.ts +1 -0
- package/templates/react-ts/src/routes/About.tsx +19 -0
- package/templates/react-ts/src/routes/Home.tsx +80 -0
- package/templates/react-ts/src/routes/NotFound.tsx +14 -0
- package/templates/react-ts/src/styles.css +13 -0
- package/templates/react-ts/tsconfig.json +25 -0
- package/templates/react-ts-server/Dockerfile +51 -0
- package/templates/react-ts-server/README.md +87 -0
- package/templates/react-ts-server/_dockerignore +8 -0
- package/templates/react-ts-server/_env.example +78 -0
- package/templates/react-ts-server/_gitignore +8 -0
- package/templates/react-ts-server/client/index.html +13 -0
- package/templates/react-ts-server/client/public/favicon.svg +4 -0
- package/templates/react-ts-server/client/src/App.tsx +66 -0
- package/templates/react-ts-server/client/src/main.tsx +18 -0
- package/templates/react-ts-server/client/src/odoro-env.d.ts +1 -0
- package/templates/react-ts-server/client/src/routes/About.tsx +19 -0
- package/templates/react-ts-server/client/src/routes/Home.tsx +148 -0
- package/templates/react-ts-server/client/src/routes/NotFound.tsx +14 -0
- package/templates/react-ts-server/client/src/styles.css +13 -0
- package/templates/react-ts-server/odoro.config.ts +21 -0
- package/templates/react-ts-server/package.json +33 -0
- package/templates/react-ts-server/scripts/dev.mjs +74 -0
- package/templates/react-ts-server/server/src/main.ts +131 -0
- package/templates/react-ts-server/server/src/modules/health/index.ts +144 -0
- package/templates/react-ts-server/server/tsconfig.json +23 -0
- package/templates/react-ts-server/tsconfig.json +27 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from 'odoro'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
// Le client vit dans son propre dossier : la racine du moteur l'y suit.
|
|
5
|
+
root: 'client',
|
|
6
|
+
alias: {
|
|
7
|
+
'@': 'src',
|
|
8
|
+
},
|
|
9
|
+
build: {
|
|
10
|
+
// Le serveur sert ce dossier en production.
|
|
11
|
+
outDir: '../dist/client',
|
|
12
|
+
},
|
|
13
|
+
server: {
|
|
14
|
+
port: 5180,
|
|
15
|
+
// Les appels d'API sont transmis au serveur, qui tourne en parallele :
|
|
16
|
+
// le navigateur ne voit qu'une seule origine, donc aucune question de CORS.
|
|
17
|
+
proxy: {
|
|
18
|
+
'/api': 'http://localhost:3001',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "odoro-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "node scripts/dev.mjs",
|
|
8
|
+
"dev:client": "odoro dev",
|
|
9
|
+
"dev:server": "tsx watch server/src/main.ts",
|
|
10
|
+
"build": "odoro build && tsc -p server/tsconfig.json",
|
|
11
|
+
"start": "node dist/server/main.js",
|
|
12
|
+
"preview": "odoro preview",
|
|
13
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p server/tsconfig.json"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@odoro-cli/libs": "^0.0.0",
|
|
17
|
+
"@odoro-cli/server": "^0.0.0",
|
|
18
|
+
"express": "^5.1.0",
|
|
19
|
+
"react": "^19.2.0",
|
|
20
|
+
"react-dom": "^19.2.0",
|
|
21
|
+
"zod": "^4.5.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/express": "^5.0.3",
|
|
25
|
+
"@types/node": "^22.18.8",
|
|
26
|
+
"@types/react": "^19.2.2",
|
|
27
|
+
"@types/react-dom": "^19.2.1",
|
|
28
|
+
"odoro": "^0.0.0",
|
|
29
|
+
"pino-pretty": "^13.0.0",
|
|
30
|
+
"tsx": "^4.20.6",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lance le client et le serveur en parallele.
|
|
3
|
+
*
|
|
4
|
+
* Un script maison plutot qu'une dependance : le besoin tient en cinquante
|
|
5
|
+
* lignes, et il vaut mieux que la sortie des deux processus reste lisible et
|
|
6
|
+
* qu'une interruption les arrete tous les deux proprement.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { spawn } from 'node:child_process'
|
|
10
|
+
|
|
11
|
+
const RESET = '\u001b[0m'
|
|
12
|
+
|
|
13
|
+
/** Processus a lancer, avec leur etiquette et leur couleur. */
|
|
14
|
+
const TASKS = [
|
|
15
|
+
{ label: 'client', color: '\u001b[35m', command: 'odoro', args: ['dev'] },
|
|
16
|
+
{
|
|
17
|
+
label: 'serveur',
|
|
18
|
+
color: '\u001b[36m',
|
|
19
|
+
command: 'tsx',
|
|
20
|
+
args: ['watch', 'server/src/main.ts'],
|
|
21
|
+
},
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
const children = []
|
|
25
|
+
|
|
26
|
+
/** Prefixe chaque ligne de sortie par l'etiquette de son processus. */
|
|
27
|
+
function pipe(stream, label, color) {
|
|
28
|
+
let buffer = ''
|
|
29
|
+
stream.setEncoding('utf8')
|
|
30
|
+
stream.on('data', (chunk) => {
|
|
31
|
+
buffer += chunk
|
|
32
|
+
const lines = buffer.split('\n')
|
|
33
|
+
buffer = lines.pop() ?? ''
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
process.stdout.write(`${color}[${label}]${RESET} ${line}\n`)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Arrete tous les processus encore vivants. */
|
|
41
|
+
function stopAll() {
|
|
42
|
+
for (const child of children) {
|
|
43
|
+
if (child.exitCode === null && !child.killed) child.kill()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const task of TASKS) {
|
|
48
|
+
const child = spawn(task.command, task.args, {
|
|
49
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
50
|
+
// Sous Windows, les binaires de node_modules sont des scripts shell.
|
|
51
|
+
shell: process.platform === 'win32',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
pipe(child.stdout, task.label, task.color)
|
|
55
|
+
pipe(child.stderr, task.label, task.color)
|
|
56
|
+
|
|
57
|
+
child.on('exit', (code) => {
|
|
58
|
+
// Si l'un s'arrete, l'autre n'a plus de raison de tourner.
|
|
59
|
+
if (code !== 0 && code !== null) {
|
|
60
|
+
process.stderr.write(`[${task.label}] arret avec le code ${code}\n`)
|
|
61
|
+
}
|
|
62
|
+
stopAll()
|
|
63
|
+
process.exitCode = code ?? 0
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
children.push(child)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
70
|
+
process.on(signal, () => {
|
|
71
|
+
stopAll()
|
|
72
|
+
process.exit(0)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Le serveur applicatif : un assemblage de modules, rien de plus.
|
|
3
|
+
*
|
|
4
|
+
* ## Ce fichier ne contient aucune logique
|
|
5
|
+
*
|
|
6
|
+
* C'est voulu. Tout ce qu'une application fait vit dans un module, et ce
|
|
7
|
+
* fichier ne fait que dire lesquels sont actifs. Activer ou desactiver une
|
|
8
|
+
* fonctionnalite tient alors en une ligne, et rien d'autre ne bouge.
|
|
9
|
+
*
|
|
10
|
+
* Un serveur ou les routes s'ajoutent directement ici finit par melanger
|
|
11
|
+
* l'assemblage et le metier, et « desactiver l'authentification » devient un
|
|
12
|
+
* travail d'archeologie plutot qu'une ligne commentee.
|
|
13
|
+
*
|
|
14
|
+
* ## En developpement et en production
|
|
15
|
+
*
|
|
16
|
+
* En developpement, ce serveur n'expose que l'API : le client est servi par
|
|
17
|
+
* Odoro, qui lui transmet les appels commencant par `/api`. En production, il
|
|
18
|
+
* sert en plus le resultat de la compilation du client — une seule chose a
|
|
19
|
+
* deployer.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { existsSync } from 'node:fs'
|
|
25
|
+
import { dirname, join, resolve } from 'node:path'
|
|
26
|
+
import { fileURLToPath } from 'node:url'
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
ConfigError,
|
|
30
|
+
createApp,
|
|
31
|
+
createContainer,
|
|
32
|
+
createLogger,
|
|
33
|
+
loadConfig,
|
|
34
|
+
} from '@odoro-cli/server'
|
|
35
|
+
import express from 'express'
|
|
36
|
+
|
|
37
|
+
import { createHealthModule } from './modules/health/index.js'
|
|
38
|
+
|
|
39
|
+
/** Racine du module compile, pour retrouver le client a cote. */
|
|
40
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
41
|
+
|
|
42
|
+
/** Assemble l'application. Exporte pour que les tests la montent sans l'ecouter. */
|
|
43
|
+
export function buildServer() {
|
|
44
|
+
const config = loadConfig()
|
|
45
|
+
|
|
46
|
+
const logger = createLogger({
|
|
47
|
+
level: config.LOG_LEVEL,
|
|
48
|
+
pretty: config.NODE_ENV === 'development',
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// La configuration et le journal sont dans le conteneur : un module les y
|
|
52
|
+
// trouve sans qu'on les lui passe de main en main a travers trois couches.
|
|
53
|
+
const container = createContainer()
|
|
54
|
+
.register('config', () => config)
|
|
55
|
+
.register('logger', () => logger)
|
|
56
|
+
|
|
57
|
+
const app = createApp({
|
|
58
|
+
config,
|
|
59
|
+
logger,
|
|
60
|
+
container: container as never,
|
|
61
|
+
modules: [
|
|
62
|
+
createHealthModule(config),
|
|
63
|
+
// Les modules du socle s'ajoutent ici, dans n'importe quel ordre :
|
|
64
|
+
// le noyau les trie selon leurs dependances.
|
|
65
|
+
//
|
|
66
|
+
// authModule,
|
|
67
|
+
// accountModule,
|
|
68
|
+
// settingsModule,
|
|
69
|
+
],
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
if (config.NODE_ENV === 'production') {
|
|
73
|
+
// `dist/server/main.js` -> `dist/client`
|
|
74
|
+
const client = resolve(HERE, '..', 'client')
|
|
75
|
+
if (existsSync(client)) {
|
|
76
|
+
app.express.use(
|
|
77
|
+
express.static(client, { index: false, maxAge: '1y', immutable: true }),
|
|
78
|
+
)
|
|
79
|
+
// Repli d'application monopage : toute route hors API rend le document,
|
|
80
|
+
// et c'est le routeur client qui decide de la suite.
|
|
81
|
+
app.express.get(/^(?!\/api\/).*/, (_request, response) => {
|
|
82
|
+
response.sendFile(join(client, 'index.html'))
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { app, config, logger }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Demarre le serveur. */
|
|
91
|
+
function main(): void {
|
|
92
|
+
let server
|
|
93
|
+
try {
|
|
94
|
+
server = buildServer()
|
|
95
|
+
} catch (cause) {
|
|
96
|
+
if (cause instanceof ConfigError) {
|
|
97
|
+
// La configuration est incomplete : le message liste tout ce qui manque,
|
|
98
|
+
// d'un coup. Rien ne sert de demarrer a moitie.
|
|
99
|
+
console.error(cause.message)
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
throw cause
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { app, config, logger } = server
|
|
106
|
+
|
|
107
|
+
const listener = app.express.listen(config.PORT, () => {
|
|
108
|
+
logger.info({ port: config.PORT, environment: config.NODE_ENV }, 'serveur a l ecoute')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Arret propre : on cesse d'accepter, on laisse finir ce qui est en cours, et
|
|
112
|
+
// on abandonne au-dela du delai plutot que de rester suspendu.
|
|
113
|
+
const stop = (signal: string): void => {
|
|
114
|
+
logger.info({ signal }, 'arret demande')
|
|
115
|
+
const deadline = setTimeout(() => {
|
|
116
|
+
logger.warn('delai depasse, arret force')
|
|
117
|
+
process.exit(1)
|
|
118
|
+
}, config.SHUTDOWN_TIMEOUT)
|
|
119
|
+
deadline.unref()
|
|
120
|
+
|
|
121
|
+
listener.close(() => {
|
|
122
|
+
clearTimeout(deadline)
|
|
123
|
+
process.exit(0)
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
process.on('SIGTERM', () => stop('SIGTERM'))
|
|
128
|
+
process.on('SIGINT', () => stop('SIGINT'))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
main()
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module de sante — et exemple de ce a quoi ressemble un module.
|
|
3
|
+
*
|
|
4
|
+
* C'est le seul module de ce template, et il est ecrit ici plutot que fourni
|
|
5
|
+
* par `@odoro-cli/server` pour une raison : ecrire un module est la premiere chose
|
|
6
|
+
* qu'on fait sur ce socle, et un exemple qu'on peut ouvrir vaut mieux qu'une
|
|
7
|
+
* page de documentation.
|
|
8
|
+
*
|
|
9
|
+
* ## Un module est une fonction de ce dont il a besoin
|
|
10
|
+
*
|
|
11
|
+
* Celui-ci recoit la configuration en parametre plutot que de lire
|
|
12
|
+
* `process.env`. C'est la regle du socle : l'environnement est valide une fois,
|
|
13
|
+
* au demarrage, et tout le reste consomme le resultat. Une lecture directe
|
|
14
|
+
* echappe a cette validation et se manifeste a la centieme requete.
|
|
15
|
+
*
|
|
16
|
+
* ## Deux points de controle, et pourquoi ils different
|
|
17
|
+
*
|
|
18
|
+
* `/api/health` repond que **le processus vit**. Il ne teste rien d'autre, et
|
|
19
|
+
* doit repondre meme quand tout le reste est casse : c'est ce qu'un
|
|
20
|
+
* orchestrateur interroge pour decider s'il faut redemarrer le conteneur. Le
|
|
21
|
+
* faire dependre de la base ferait redemarrer un serveur parfaitement sain
|
|
22
|
+
* chaque fois que la base hoquette — et un redemarrage ne repare pas une base.
|
|
23
|
+
*
|
|
24
|
+
* `/api/ready` repond que **le service peut travailler**, et rend 503 tant
|
|
25
|
+
* qu'il manque quelque chose : c'est ce qu'un repartiteur interroge pour
|
|
26
|
+
* decider s'il peut envoyer du trafic.
|
|
27
|
+
*
|
|
28
|
+
* Confondre les deux donne l'un des deux defauts : un service qui redemarre en
|
|
29
|
+
* boucle pendant un incident de base, ou un repartiteur qui envoie du trafic a
|
|
30
|
+
* un service incapable de repondre.
|
|
31
|
+
*
|
|
32
|
+
* ## Pourquoi `/ready` echoue au premier demarrage
|
|
33
|
+
*
|
|
34
|
+
* Un projet fraichement echafaude n'a pas encore de `DATABASE_URL`. Le client
|
|
35
|
+
* demarre, l'interface s'affiche, et `/api/ready` dit ce qui manque. On voit
|
|
36
|
+
* donc quelque chose des la premiere minute, et on sait exactement ce qu'il
|
|
37
|
+
* reste a faire.
|
|
38
|
+
*
|
|
39
|
+
* @module
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
ServiceUnavailableError,
|
|
44
|
+
defineModule,
|
|
45
|
+
route,
|
|
46
|
+
type KernelConfig,
|
|
47
|
+
} from '@odoro-cli/server'
|
|
48
|
+
import { z } from 'zod'
|
|
49
|
+
|
|
50
|
+
/** Ce que rend le controle de vie. */
|
|
51
|
+
const liveness = z.object({
|
|
52
|
+
status: z.literal('ok'),
|
|
53
|
+
environment: z.string(),
|
|
54
|
+
uptime: z.number(),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
/** Ce que rend le controle de disponibilite, quand tout repond. */
|
|
58
|
+
const readiness = z.object({
|
|
59
|
+
ready: z.literal(true),
|
|
60
|
+
dependencies: z.array(z.object({ name: z.string(), detail: z.string() })),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
/** Une dependance et son etat. */
|
|
64
|
+
interface Dependency {
|
|
65
|
+
readonly name: string
|
|
66
|
+
readonly ready: boolean
|
|
67
|
+
readonly detail: string
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Etat des dependances.
|
|
72
|
+
*
|
|
73
|
+
* La base n'est pas encore interrogee — le socle n'a pas sa couche de
|
|
74
|
+
* persistance. Ce qui est verifie ici est sa **configuration**, ce qui suffit
|
|
75
|
+
* a distinguer un projet qui n'a jamais recu d'URL d'un projet configure.
|
|
76
|
+
* Quand la persistance arrivera, c'est cette fonction qui apprendra a ouvrir
|
|
77
|
+
* une connexion, et rien d'autre ne changera.
|
|
78
|
+
*/
|
|
79
|
+
function inspect(config: KernelConfig): readonly Dependency[] {
|
|
80
|
+
const url = config.DATABASE_URL.trim()
|
|
81
|
+
return [
|
|
82
|
+
{
|
|
83
|
+
name: 'database',
|
|
84
|
+
ready: url.length > 0,
|
|
85
|
+
detail:
|
|
86
|
+
url.length > 0
|
|
87
|
+
? 'URL configuree'
|
|
88
|
+
: 'DATABASE_URL absente — voir .env.example, ou lancer `odoro db:create`',
|
|
89
|
+
},
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Construit le module.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* createApp({ modules: [createHealthModule(config)], … })
|
|
98
|
+
*/
|
|
99
|
+
export function createHealthModule(config: KernelConfig) {
|
|
100
|
+
return defineModule({
|
|
101
|
+
name: 'health',
|
|
102
|
+
routes: [
|
|
103
|
+
route({
|
|
104
|
+
name: 'health.live',
|
|
105
|
+
method: 'GET',
|
|
106
|
+
path: '/api/health',
|
|
107
|
+
auth: 'public',
|
|
108
|
+
summary: 'Le processus repond.',
|
|
109
|
+
output: liveness,
|
|
110
|
+
handler: () => ({
|
|
111
|
+
status: 'ok' as const,
|
|
112
|
+
environment: config.NODE_ENV,
|
|
113
|
+
uptime: Math.round(process.uptime()),
|
|
114
|
+
}),
|
|
115
|
+
}),
|
|
116
|
+
|
|
117
|
+
route({
|
|
118
|
+
name: 'health.ready',
|
|
119
|
+
method: 'GET',
|
|
120
|
+
path: '/api/ready',
|
|
121
|
+
auth: 'public',
|
|
122
|
+
summary: 'Les dependances repondent.',
|
|
123
|
+
output: readiness,
|
|
124
|
+
handler: () => {
|
|
125
|
+
const dependencies = inspect(config)
|
|
126
|
+
const manquantes = dependencies.filter((d) => !d.ready)
|
|
127
|
+
|
|
128
|
+
if (manquantes.length > 0) {
|
|
129
|
+
// 503 et non 500 : la demande etait valide, c'est le service qui
|
|
130
|
+
// ne peut pas encore y repondre.
|
|
131
|
+
throw new ServiceUnavailableError(
|
|
132
|
+
manquantes.map((d) => `${d.name} — ${d.detail}`).join(' ; '),
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
ready: true as const,
|
|
138
|
+
dependencies: dependencies.map(({ name, detail }) => ({ name, detail })),
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
] as never,
|
|
143
|
+
})
|
|
144
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2023"],
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"verbatimModuleSyntax": true,
|
|
9
|
+
"types": ["node"],
|
|
10
|
+
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noUncheckedIndexedAccess": true,
|
|
13
|
+
"noUnusedLocals": true,
|
|
14
|
+
"noUnusedParameters": true,
|
|
15
|
+
"noFallthroughCasesInSwitch": true,
|
|
16
|
+
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"outDir": "../dist/server",
|
|
19
|
+
"rootDir": "./src",
|
|
20
|
+
"sourceMap": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"verbatimModuleSyntax": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noUncheckedIndexedAccess": true,
|
|
16
|
+
"noUnusedLocals": true,
|
|
17
|
+
"noUnusedParameters": true,
|
|
18
|
+
"noFallthroughCasesInSwitch": true,
|
|
19
|
+
|
|
20
|
+
"skipLibCheck": true,
|
|
21
|
+
"baseUrl": ".",
|
|
22
|
+
"paths": {
|
|
23
|
+
"@/*": ["./client/src/*"]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"include": ["client/src", "odoro.config.ts"]
|
|
27
|
+
}
|