@base44-preview/cli 0.0.1-pr.13.d022278 → 0.0.1-pr.14.723fe70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { intro, log, spinner } from "@clack/prompts";
|
|
4
|
+
import { cancel, intro, isCancel, log, spinner, text } from "@clack/prompts";
|
|
5
5
|
import pWaitFor from "p-wait-for";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import { dirname, join } from "node:path";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
+
import { config } from "dotenv";
|
|
9
10
|
import ky from "ky";
|
|
10
11
|
import { access, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
11
12
|
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
12
13
|
import { globby } from "globby";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import ejs from "ejs";
|
|
13
16
|
|
|
17
|
+
//#region rolldown:runtime
|
|
18
|
+
var __defProp = Object.defineProperty;
|
|
19
|
+
var __exportAll = (all, symbols) => {
|
|
20
|
+
let target = {};
|
|
21
|
+
for (var name in all) {
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (symbols) {
|
|
28
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
29
|
+
}
|
|
30
|
+
return target;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
14
34
|
//#region src/core/auth/schema.ts
|
|
15
35
|
const AuthDataSchema = z.object({
|
|
16
36
|
accessToken: z.string().min(1, "Token cannot be empty"),
|
|
@@ -73,9 +93,11 @@ var AuthValidationError = class extends Error {
|
|
|
73
93
|
};
|
|
74
94
|
|
|
75
95
|
//#endregion
|
|
76
|
-
//#region src/core/
|
|
96
|
+
//#region src/core/config.ts
|
|
77
97
|
const PROJECT_SUBDIR = "base44";
|
|
78
98
|
const FUNCTION_CONFIG_FILE = "function.jsonc";
|
|
99
|
+
const AUTH_CLIENT_ID = "base44_cli";
|
|
100
|
+
const DEFAULT_API_URL = "https://app.base44.com";
|
|
79
101
|
function getBase44Dir() {
|
|
80
102
|
return join(homedir(), ".base44");
|
|
81
103
|
}
|
|
@@ -90,15 +112,34 @@ function getProjectConfigPatterns() {
|
|
|
90
112
|
"config.json"
|
|
91
113
|
];
|
|
92
114
|
}
|
|
93
|
-
|
|
94
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Load .env.local from the project root if it exists.
|
|
117
|
+
* Values won't override existing process.env variables.
|
|
118
|
+
* Safe to call multiple times - only loads once.
|
|
119
|
+
*/
|
|
120
|
+
async function loadProjectEnv(projectRoot) {
|
|
121
|
+
const { findProjectRoot: findProjectRoot$1 } = await Promise.resolve().then(() => config_exports);
|
|
122
|
+
const found = projectRoot ? { root: projectRoot } : await findProjectRoot$1();
|
|
123
|
+
if (!found) return;
|
|
124
|
+
config({
|
|
125
|
+
path: join(found.root, ".env.local"),
|
|
126
|
+
override: false
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get the Base44 API URL.
|
|
131
|
+
* Priority: process.env.BASE44_API_URL > .env.local > default
|
|
132
|
+
*/
|
|
95
133
|
function getBase44ApiUrl() {
|
|
96
134
|
return process.env.BASE44_API_URL || DEFAULT_API_URL;
|
|
97
135
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Get the Base44 Client ID (app ID).
|
|
138
|
+
* Priority: process.env.BASE44_CLIENT_ID > .env.local
|
|
139
|
+
* Returns undefined if not set.
|
|
140
|
+
*/
|
|
141
|
+
function getBase44ClientId() {
|
|
142
|
+
return process.env.BASE44_CLIENT_ID;
|
|
102
143
|
}
|
|
103
144
|
|
|
104
145
|
//#endregion
|
|
@@ -181,8 +222,18 @@ async function getUserInfo(accessToken) {
|
|
|
181
222
|
|
|
182
223
|
//#endregion
|
|
183
224
|
//#region src/core/utils/fs.ts
|
|
184
|
-
function pathExists(path) {
|
|
185
|
-
|
|
225
|
+
async function pathExists(path) {
|
|
226
|
+
try {
|
|
227
|
+
await access(path);
|
|
228
|
+
return true;
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function writeFile$1(filePath, content) {
|
|
234
|
+
const dir = dirname(filePath);
|
|
235
|
+
if (!await pathExists(dir)) await mkdir(dir, { recursive: true });
|
|
236
|
+
await writeFile(filePath, content, "utf-8");
|
|
186
237
|
}
|
|
187
238
|
async function readJsonFile(filePath) {
|
|
188
239
|
if (!await pathExists(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
@@ -201,21 +252,13 @@ async function readJsonFile(filePath) {
|
|
|
201
252
|
}
|
|
202
253
|
}
|
|
203
254
|
async function writeJsonFile(filePath, data) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
208
|
-
} catch (error) {
|
|
209
|
-
throw new Error(`Failed to write file ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
210
|
-
}
|
|
255
|
+
const dir = dirname(filePath);
|
|
256
|
+
if (!await pathExists(dir)) await mkdir(dir, { recursive: true });
|
|
257
|
+
await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
211
258
|
}
|
|
212
259
|
async function deleteFile(filePath) {
|
|
213
260
|
if (!await pathExists(filePath)) return;
|
|
214
|
-
|
|
215
|
-
await unlink(filePath);
|
|
216
|
-
} catch (error) {
|
|
217
|
-
throw new Error(`Failed to delete file ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
218
|
-
}
|
|
261
|
+
await unlink(filePath);
|
|
219
262
|
}
|
|
220
263
|
|
|
221
264
|
//#endregion
|
|
@@ -290,11 +333,13 @@ const base44Color = chalk.bgHex("#E86B3C");
|
|
|
290
333
|
/**
|
|
291
334
|
* Wraps a command function with the Base44 intro banner.
|
|
292
335
|
* All CLI commands should use this utility to ensure consistent branding.
|
|
336
|
+
* Also loads .env.local from the project root if available.
|
|
293
337
|
*
|
|
294
338
|
* @param commandFn - The async function to execute as the command
|
|
295
339
|
*/
|
|
296
340
|
async function runCommand(commandFn) {
|
|
297
341
|
intro(base44Color(" Base 44 "));
|
|
342
|
+
await loadProjectEnv();
|
|
298
343
|
try {
|
|
299
344
|
await commandFn();
|
|
300
345
|
} catch (e) {
|
|
@@ -328,6 +373,42 @@ async function runTask(startMessage, operation, options) {
|
|
|
328
373
|
}
|
|
329
374
|
}
|
|
330
375
|
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/cli/utils/prompts.ts
|
|
378
|
+
/**
|
|
379
|
+
* Handles prompt cancellation by exiting gracefully.
|
|
380
|
+
*/
|
|
381
|
+
function handleCancel(value) {
|
|
382
|
+
if (isCancel(value)) {
|
|
383
|
+
cancel("Operation cancelled.");
|
|
384
|
+
process.exit(0);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Wrapper around @clack/prompts text() that handles cancellation automatically.
|
|
389
|
+
* Returns the string value directly, exits process if cancelled.
|
|
390
|
+
*/
|
|
391
|
+
async function textPrompt(options) {
|
|
392
|
+
const value = await text(options);
|
|
393
|
+
handleCancel(value);
|
|
394
|
+
return value;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/cli/utils/banner.ts
|
|
399
|
+
const orange = chalk.hex("#E86B3C");
|
|
400
|
+
const BANNER = `
|
|
401
|
+
${orange("██████╗ █████╗ ███████╗███████╗ ██╗ ██╗██╗ ██╗")}
|
|
402
|
+
${orange("██╔══██╗██╔══██╗██╔════╝██╔════╝ ██║ ██║██║ ██║")}
|
|
403
|
+
${orange("██████╔╝███████║███████╗█████╗ ███████║███████║")}
|
|
404
|
+
${orange("██╔══██╗██╔══██║╚════██║██╔══╝ ╚════██║╚════██║")}
|
|
405
|
+
${orange("██████╔╝██║ ██║███████║███████╗ ██║ ██║")}
|
|
406
|
+
${orange("╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝")}
|
|
407
|
+
`;
|
|
408
|
+
function printBanner() {
|
|
409
|
+
console.log(BANNER);
|
|
410
|
+
}
|
|
411
|
+
|
|
331
412
|
//#endregion
|
|
332
413
|
//#region src/cli/commands/auth/login.ts
|
|
333
414
|
async function generateAndDisplayDeviceCode() {
|
|
@@ -496,26 +577,18 @@ const base44Client = ky.create({
|
|
|
496
577
|
afterResponse: [handleUnauthorized]
|
|
497
578
|
}
|
|
498
579
|
});
|
|
499
|
-
let _appClient = null;
|
|
500
580
|
/**
|
|
501
581
|
* Returns an HTTP client scoped to the current app.
|
|
502
|
-
* Lazily initialized on first call (app ID must be set via BASE44_CLIENT_ID env var).
|
|
503
582
|
*/
|
|
504
583
|
function getAppClient() {
|
|
505
|
-
|
|
506
|
-
return _appClient;
|
|
584
|
+
return base44Client.extend({ prefixUrl: new URL(`/api/apps/${getBase44ClientId()}/`, getBase44ApiUrl()).href });
|
|
507
585
|
}
|
|
508
586
|
|
|
509
587
|
//#endregion
|
|
510
588
|
//#region src/core/resources/entity/api.ts
|
|
511
|
-
const appClient = getAppClient();
|
|
512
589
|
async function pushEntities(entities) {
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
...acc,
|
|
516
|
-
[current.name]: current
|
|
517
|
-
};
|
|
518
|
-
}, {});
|
|
590
|
+
const appClient = getAppClient();
|
|
591
|
+
const schemaSyncPayload = Object.fromEntries(entities.map((entity) => [entity.name, entity]));
|
|
519
592
|
const response = await appClient.put("entities-schemas/sync-all", {
|
|
520
593
|
json: { entityNameToSchema: schemaSyncPayload },
|
|
521
594
|
throwHttpErrors: false
|
|
@@ -591,13 +664,15 @@ async function readAllFunctions(functionsDir) {
|
|
|
591
664
|
|
|
592
665
|
//#endregion
|
|
593
666
|
//#region src/core/resources/function/resource.ts
|
|
594
|
-
const functionResource = {
|
|
595
|
-
readAll: readAllFunctions,
|
|
596
|
-
push: async () => {}
|
|
597
|
-
};
|
|
667
|
+
const functionResource = { readAll: readAllFunctions };
|
|
598
668
|
|
|
599
669
|
//#endregion
|
|
600
|
-
//#region src/core/config
|
|
670
|
+
//#region src/core/project/config.ts
|
|
671
|
+
var config_exports = /* @__PURE__ */ __exportAll({
|
|
672
|
+
ProjectConfigSchema: () => ProjectConfigSchema,
|
|
673
|
+
findProjectRoot: () => findProjectRoot,
|
|
674
|
+
readProjectConfig: () => readProjectConfig
|
|
675
|
+
});
|
|
601
676
|
const ProjectConfigSchema = z.looseObject({
|
|
602
677
|
name: z.string().min(1, "Project name cannot be empty"),
|
|
603
678
|
entitiesDir: z.string().default("./entities"),
|
|
@@ -669,7 +744,7 @@ const showProjectCommand = new Command("show-project").description("Display proj
|
|
|
669
744
|
});
|
|
670
745
|
|
|
671
746
|
//#endregion
|
|
672
|
-
//#region src/core/
|
|
747
|
+
//#region src/core/project/schema.ts
|
|
673
748
|
const SiteConfigSchema = z.object({
|
|
674
749
|
buildCommand: z.string().optional(),
|
|
675
750
|
serveCommand: z.string().optional(),
|
|
@@ -682,6 +757,60 @@ const AppConfigSchema = z.object({
|
|
|
682
757
|
site: SiteConfigSchema.optional(),
|
|
683
758
|
domains: z.array(z.string()).optional()
|
|
684
759
|
});
|
|
760
|
+
const CreateProjectResponseSchema = z.looseObject({ id: z.string() });
|
|
761
|
+
|
|
762
|
+
//#endregion
|
|
763
|
+
//#region src/core/project/api.ts
|
|
764
|
+
async function createProject(projectName, description) {
|
|
765
|
+
const response = await base44Client.post("api/apps", { json: {
|
|
766
|
+
name: projectName,
|
|
767
|
+
user_description: description ?? `Backend for '${projectName}'`,
|
|
768
|
+
app_type: "baas"
|
|
769
|
+
} });
|
|
770
|
+
return { projectId: CreateProjectResponseSchema.parse(await response.json()).id };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
//#endregion
|
|
774
|
+
//#region src/core/project/templates/index.ts
|
|
775
|
+
const TEMPLATES_DIR = join(dirname(fileURLToPath(import.meta.url)), "templates");
|
|
776
|
+
const CONFIG_TEMPLATE_PATH = join(TEMPLATES_DIR, "config.jsonc.ejs");
|
|
777
|
+
const ENV_TEMPLATE_PATH = join(TEMPLATES_DIR, "env.local.ejs");
|
|
778
|
+
async function renderConfigTemplate(data) {
|
|
779
|
+
return ejs.renderFile(CONFIG_TEMPLATE_PATH, data);
|
|
780
|
+
}
|
|
781
|
+
async function renderEnvTemplate(data) {
|
|
782
|
+
return ejs.renderFile(ENV_TEMPLATE_PATH, data);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region src/core/project/init.ts
|
|
787
|
+
/**
|
|
788
|
+
* Initialize a new Base44 project.
|
|
789
|
+
* Creates the base44 directory, config.jsonc, and .env.local files.
|
|
790
|
+
*/
|
|
791
|
+
async function initProject(options) {
|
|
792
|
+
const { name, description, path: basePath } = options;
|
|
793
|
+
const projectDir = join(basePath, PROJECT_SUBDIR);
|
|
794
|
+
const configPath = join(projectDir, "config.jsonc");
|
|
795
|
+
const envPath = join(projectDir, ".env.local");
|
|
796
|
+
const existingConfigs = await globby(getProjectConfigPatterns(), {
|
|
797
|
+
cwd: basePath,
|
|
798
|
+
absolute: true
|
|
799
|
+
});
|
|
800
|
+
if (existingConfigs.length > 0) throw new Error(`A Base44 project already exists at ${existingConfigs[0]}. Please choose a different location.`);
|
|
801
|
+
const { projectId } = await createProject(name, description);
|
|
802
|
+
await writeFile$1(configPath, await renderConfigTemplate({
|
|
803
|
+
name,
|
|
804
|
+
description
|
|
805
|
+
}));
|
|
806
|
+
await writeFile$1(envPath, await renderEnvTemplate({ projectId }));
|
|
807
|
+
return {
|
|
808
|
+
projectId,
|
|
809
|
+
projectDir,
|
|
810
|
+
configPath,
|
|
811
|
+
envPath
|
|
812
|
+
};
|
|
813
|
+
}
|
|
685
814
|
|
|
686
815
|
//#endregion
|
|
687
816
|
//#region src/cli/commands/entities/push.ts
|
|
@@ -707,6 +836,50 @@ const entitiesPushCommand = new Command("entities").description("Manage project
|
|
|
707
836
|
await runCommand(pushEntitiesAction);
|
|
708
837
|
}));
|
|
709
838
|
|
|
839
|
+
//#endregion
|
|
840
|
+
//#region src/cli/commands/project/init.ts
|
|
841
|
+
async function init() {
|
|
842
|
+
printBanner();
|
|
843
|
+
await loadProjectEnv();
|
|
844
|
+
const name = await textPrompt({
|
|
845
|
+
message: "What is the name of your project?",
|
|
846
|
+
placeholder: "my-app-backend",
|
|
847
|
+
validate: (value) => {
|
|
848
|
+
if (!value || value.trim().length === 0) return "Project name is required";
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
const description = await textPrompt({
|
|
852
|
+
message: "Project description (optional)",
|
|
853
|
+
placeholder: "A brief description of your project"
|
|
854
|
+
});
|
|
855
|
+
const defaultPath = "./";
|
|
856
|
+
const resolvedPath = resolve(await textPrompt({
|
|
857
|
+
message: "Where should we create the base44 folder?",
|
|
858
|
+
placeholder: defaultPath,
|
|
859
|
+
initialValue: defaultPath
|
|
860
|
+
}) || defaultPath);
|
|
861
|
+
await runTask("Creating project...", async () => {
|
|
862
|
+
return await initProject({
|
|
863
|
+
name: name.trim(),
|
|
864
|
+
description: description ? description.trim() : void 0,
|
|
865
|
+
path: resolvedPath
|
|
866
|
+
});
|
|
867
|
+
}, {
|
|
868
|
+
successMessage: "Project created successfully",
|
|
869
|
+
errorMessage: "Failed to create project"
|
|
870
|
+
});
|
|
871
|
+
log.success(`Project ${chalk.bold(name)} has been initialized!`);
|
|
872
|
+
}
|
|
873
|
+
const initCommand = new Command("init").alias("create").description("Initialize a new Base44 project").action(async () => {
|
|
874
|
+
try {
|
|
875
|
+
await init();
|
|
876
|
+
} catch (e) {
|
|
877
|
+
if (e instanceof Error) log.error(e.stack ?? e.message);
|
|
878
|
+
else log.error(String(e));
|
|
879
|
+
process.exit(1);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
|
|
710
883
|
//#endregion
|
|
711
884
|
//#region package.json
|
|
712
885
|
var version = "0.0.1";
|
|
@@ -718,9 +891,10 @@ program.name("base44").description("Base44 CLI - Unified interface for managing
|
|
|
718
891
|
program.addCommand(loginCommand);
|
|
719
892
|
program.addCommand(whoamiCommand);
|
|
720
893
|
program.addCommand(logoutCommand);
|
|
894
|
+
program.addCommand(initCommand);
|
|
721
895
|
program.addCommand(showProjectCommand);
|
|
722
896
|
program.addCommand(entitiesPushCommand);
|
|
723
897
|
program.parse();
|
|
724
898
|
|
|
725
899
|
//#endregion
|
|
726
|
-
export {
|
|
900
|
+
export { findProjectRoot as n, readProjectConfig as r, ProjectConfigSchema as t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Base44 Project Configuration
|
|
2
|
+
// JSONC enables inline documentation and discoverability directly in config files.
|
|
3
|
+
// Commented-out properties show available options you can enable.
|
|
4
|
+
|
|
5
|
+
{
|
|
6
|
+
"name": "<%= name %>"<% if (description) { %>,
|
|
7
|
+
"description": "<%= description %>"<% } %>
|
|
8
|
+
|
|
9
|
+
// Site/hosting configuration
|
|
10
|
+
// Docs: https://docs.base44.com/configuration/hosting
|
|
11
|
+
// "site": {
|
|
12
|
+
// "buildCommand": "npm run build",
|
|
13
|
+
// "serveCommand": "npm run dev",
|
|
14
|
+
// "outputDirectory": "./dist",
|
|
15
|
+
// "installCommand": "npm ci"
|
|
16
|
+
// }
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import ejs from "ejs";
|
|
4
|
+
|
|
5
|
+
// After bundling, import.meta.url points to dist/cli/index.js
|
|
6
|
+
// Templates are copied to dist/cli/templates/
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const TEMPLATES_DIR = join(__dirname, "templates");
|
|
9
|
+
|
|
10
|
+
const CONFIG_TEMPLATE_PATH = join(TEMPLATES_DIR, "config.jsonc.ejs");
|
|
11
|
+
const ENV_TEMPLATE_PATH = join(TEMPLATES_DIR, "env.local.ejs");
|
|
12
|
+
|
|
13
|
+
interface ConfigTemplateData {
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface EnvTemplateData {
|
|
19
|
+
projectId: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function renderConfigTemplate(
|
|
23
|
+
data: ConfigTemplateData
|
|
24
|
+
): Promise<string> {
|
|
25
|
+
return ejs.renderFile(CONFIG_TEMPLATE_PATH, data);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function renderEnvTemplate(data: EnvTemplateData): Promise<string> {
|
|
29
|
+
return ejs.renderFile(ENV_TEMPLATE_PATH, data);
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base44-preview/cli",
|
|
3
|
-
"version": "0.0.1-pr.
|
|
3
|
+
"version": "0.0.1-pr.14.723fe70",
|
|
4
4
|
"description": "Base44 CLI - Unified interface for managing Base44 applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cli/index.js",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"@clack/prompts": "^0.11.0",
|
|
37
37
|
"chalk": "^5.6.2",
|
|
38
38
|
"commander": "^12.1.0",
|
|
39
|
+
"dotenv": "^17.2.3",
|
|
40
|
+
"ejs": "^3.1.10",
|
|
39
41
|
"globby": "^16.1.0",
|
|
40
42
|
"jsonc-parser": "^3.3.1",
|
|
41
43
|
"ky": "^1.14.2",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@stylistic/eslint-plugin": "^5.6.1",
|
|
49
|
+
"@types/ejs": "^3.1.5",
|
|
47
50
|
"@types/node": "^22.10.5",
|
|
48
51
|
"@typescript-eslint/eslint-plugin": "^8.51.0",
|
|
49
52
|
"@typescript-eslint/parser": "^8.51.0",
|