@lpdjs/firestore-repo-service 2.6.7 → 2.6.9
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/servers/hono/cli.cjs +62 -52
- package/dist/servers/hono/cli.cjs.map +1 -1
- package/dist/servers/hono/cli.js +62 -52
- package/dist/servers/hono/cli.js.map +1 -1
- package/dist/servers/hono/index.cjs +7 -7
- package/dist/servers/hono/index.cjs.map +1 -1
- package/dist/servers/hono/index.d.cts +470 -11
- package/dist/servers/hono/index.d.ts +470 -11
- package/dist/servers/hono/index.js +7 -7
- package/dist/servers/hono/index.js.map +1 -1
- package/package.json +1 -1
package/dist/servers/hono/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {existsSync,mkdirSync,writeFileSync,readFileSync,readdirSync,statSync}from'fs';import {resolve,dirname,relative,join,sep}from'path';import {stdin,stdout}from'process';import {createInterface}from'readline/promises';var O={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function M(e,t=O){let s=new Set(t.skipSegments.map(o=>o.toLowerCase()));return "/"+e.split("/").filter(Boolean).filter(o=>!s.has(o.toLowerCase())).map(o=>t.casing==="kebab"?
|
|
2
|
+
import {existsSync,mkdirSync,writeFileSync,readFileSync,readdirSync,statSync}from'fs';import {resolve,dirname,relative,join,sep}from'path';import {stdin,stdout}from'process';import {createInterface}from'readline/promises';var O={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function M(e,t=O){let s=new Set(t.skipSegments.map(o=>o.toLowerCase()));return "/"+e.split("/").filter(Boolean).filter(o=>!s.has(o.toLowerCase())).map(o=>t.casing==="kebab"?oe(o):o).join("/")}function oe(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function W(e,t,s){let r=H(e),o=H(t),i=0;for(;i<r.length&&i<o.length&&r[i]===o[i];)i++;let c=r.length-i,n=o.slice(i),d=(n[n.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),f=s===""?d:`${d}${s}`;return n[n.length-1]=f,(c===0?"./":"../".repeat(c))+n.join("/")}function H(e){return e.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((s,r)=>!(r===0&&s===""))}var ae="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function J(e,t){let s=dirname(t.outFile);mkdirSync(s,{recursive:true});let r=t.banner??ae,o=(t.now??new Date).toISOString(),i=t.importExtension,c=[],n=[],a=[];e.forEach((f,p)=>{let P=W(s,f.absPath,i),y=M(f.relDir,t.derive);c.push(`import mod${p} from ${JSON.stringify(P)};`),n.push(` { __derivedPath: ${JSON.stringify(y)}, mod: mod${p} },`),a.push({source:f.relPath,url:y});});let d=`${r}// Generated at ${o} \u2014 ${e.length} route file${e.length===1?"":"s"}.
|
|
3
3
|
|
|
4
4
|
import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
5
5
|
|
|
@@ -8,16 +8,16 @@ import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-serv
|
|
|
8
8
|
|
|
9
9
|
`:`
|
|
10
10
|
`)+`const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [
|
|
11
|
-
`+
|
|
12
|
-
`)+(
|
|
11
|
+
`+n.join(`
|
|
12
|
+
`)+(n.length?`
|
|
13
13
|
`:"")+`];
|
|
14
14
|
|
|
15
15
|
export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {
|
|
16
16
|
const list = Array.isArray(mod) ? mod : [mod];
|
|
17
17
|
return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));
|
|
18
18
|
});
|
|
19
|
-
`;return writeFileSync(t.outFile,d,"utf8"),{outFile:t.outFile,routeCount:e.length,derivedPaths:a}}var
|
|
20
|
-
`,"utf8"),s}function
|
|
19
|
+
`;return writeFileSync(t.outFile,d,"utf8"),{outFile:t.outFile,routeCount:e.length,derivedPaths:a}}var E={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function K(e,t=E){let s=[];return V(e,e,t,s),s.sort((r,o)=>r.relPath.localeCompare(o.relPath)),s}function V(e,t,s,r){let o;try{o=readdirSync(t);}catch{return}for(let i of o){if(s.excludeSegments.includes(i))continue;let c=join(t,i),n;try{n=statSync(c);}catch{continue}if(n.isDirectory())V(e,c,s,r);else if(n.isFile()&&i===s.routesFile){let a=relative(e,c).split(sep).join("/"),d=a.replace(/\/?[^/]+$/,"");r.push({absPath:c,relPath:a,relDir:d});}}}var ee=".frsrc.json";function T(e=process.cwd()){let t=resolve(e,ee);if(!existsSync(t))return {};try{return JSON.parse(readFileSync(t,"utf8"))}catch{return {}}}function he(e,t=process.cwd()){let s=resolve(t,ee),o={...T(t),...e};return writeFileSync(s,`${JSON.stringify(o,null,2)}
|
|
20
|
+
`,"utf8"),s}function ve(e){let[t,...s]=e,r={};for(let o=0;o<s.length;o++){let i=s[o];if(!i.startsWith("--"))continue;let c=i.slice(2),n=s[o+1];n&&!n.startsWith("--")?(r[c]=n,o++):r[c]=true;}return {command:t??"help",flags:r}}function Z(){console.log(`frs \u2014 Hono file-based codegen
|
|
21
21
|
|
|
22
22
|
Usage:
|
|
23
23
|
frs init [flags]
|
|
@@ -62,8 +62,10 @@ Flags (new <name>):
|
|
|
62
62
|
--usecase-folder <name>
|
|
63
63
|
Parent folder under <domain>.
|
|
64
64
|
Default: .frsrc.json "useCaseFolder" or useCases
|
|
65
|
-
--with-usecase Also scaffold a sibling useCase.ts file
|
|
66
|
-
|
|
65
|
+
--with-usecase Also scaffold a sibling <domain>.<name>.useCase.ts file
|
|
66
|
+
(default: true)
|
|
67
|
+
--with-test Also scaffold a sibling <domain>.<name>.useCase.test.ts
|
|
68
|
+
(Vitest, default: true)
|
|
67
69
|
--apis-import <path> Import path for the registry (default: auto-detect
|
|
68
70
|
../../../../apis.js \u2014 adjust if your layout differs)
|
|
69
71
|
--force Overwrite if files already exist
|
|
@@ -81,8 +83,8 @@ Examples:
|
|
|
81
83
|
frs new createPost --domain posts --method post
|
|
82
84
|
frs new listPosts --domain posts --method get --api v1
|
|
83
85
|
frs add service postRepo
|
|
84
|
-
`);}function
|
|
85
|
-
* ${
|
|
86
|
+
`);}function Q(e){if(typeof e=="string")return e.split(",").map(t=>t.trim()).filter(Boolean)}function m(e){return typeof e=="string"?e:void 0}function te(e){if(e||!stdin.isTTY)return {ask:async(s,r)=>r??"",askChoice:async(s,r,o)=>o??"",askBool:async(s,r)=>r,close:()=>{}};let t=createInterface({input:stdin,output:stdout});return {async ask(s,r){let o=r?` (${r})`:"";return (await t.question(`? ${s}${o} \u203A `)).trim()||r||""},async askChoice(s,r,o){let i=` [${r.join("/")}${o?`, default: ${o}`:""}]`;for(;;){let c=(await t.question(`? ${s}${i} \u203A `)).trim().toLowerCase();if(!c&&o)return o;if(r.includes(c))return c;console.log(` invalid choice \u2014 pick one of: ${r.join(", ")}`);}},async askBool(s,r){let o=` (${r?"Y/n":"y/N"})`,i=(await t.question(`? ${s}${o} \u203A `)).trim().toLowerCase();return i?i==="y"||i==="yes"||i==="true":r},close:()=>t.close()}}async function ye(e){let t=T(),s=m(e.root)??t.root;s||(console.error("[frs] --root is required (or run `frs init` to write it to .frsrc.json)"),process.exit(2));let r=resolve(process.cwd(),s);existsSync(r)||(console.error(`[frs] root not found: ${r}`),process.exit(2));let o=m(e.out)??t.out??"__generated__/routes.ts",i=Q(e.skip)??O.skipSegments,c=m(e.casing)==="kebab"?"kebab":O.casing,n={skipSegments:i,casing:c},a=m(e.ext)??".js",d=Q(e.exclude)??E.excludeSegments,f=m(e["routes-file"])??E.routesFile,P=K(r,{routesFile:f,excludeSegments:d});P.length===0&&console.warn(`[frs] no "${f}" files found under ${r} \u2014 generated an empty manifest.`);let y=J(P,{outFile:resolve(r,o),derive:n,importExtension:a});if(!e.silent){console.log(`[frs] wrote ${y.outFile} (${y.routeCount} route${y.routeCount===1?"":"s"})`);for(let{source:S,url:g}of y.derivedPaths)console.log(` ${g.padEnd(48)} \u2190 ${S}`);}}async function $e(e,t){let s=t.yes===true,r=te(s),o=T();try{let i=e&&!e.startsWith("--")?e:void 0;i||(i=(await r.ask("Route name (e.g. createPost)")).trim(),i||(console.error("[frs] route name is required"),process.exit(2)));let c=m(t.domain);c||(c=(await r.ask("Domain name (e.g. posts)")).trim(),c||(console.error("[frs] --domain is required"),process.exit(2)));let n=m(t.root)??o.root??"src/domains",a=m(t.method)?.toLowerCase();a||(a=await r.askChoice("HTTP method",["get","post","put","patch","delete"],"post")),["get","post","put","patch","delete"].includes(a)||(console.error(`[frs] invalid --method: ${a}`),process.exit(2));let d=m(t.api);if(!d){let v=o.apis?.[0]??"v1";d=(await r.ask("API tag",v)).trim()||v;}let f=m(t["usecase-folder"])??o.useCaseFolder??"useCases",p=t["with-usecase"]===void 0?s?!0:await r.askBool("Scaffold useCase.ts?",!0):t["with-usecase"]!==!1,P=t["with-test"]===void 0?s||!p?p:await r.askBool("Scaffold useCase.test.ts (Vitest)?",!0):t["with-test"]!==!1,y=t.force===!0,S=resolve(process.cwd(),n),g=resolve(S,c,f,i),A=resolve(g,"routes.ts"),$=`${c}.${i}.useCase`,R=resolve(g,`${$}.ts`),k=resolve(g,`${$}.test.ts`);mkdirSync(g,{recursive:!0});let F=v=>v.charAt(0).toUpperCase()+v.slice(1),w=`${F(c)}${F(i)}UseCase`,D="@lpdjs/firestore-repo-service/servers/hono",h=Se(S,g),b=a==="get"?"// GET \u2192 lu depuis les query params":`// ${a.toUpperCase()} \u2192 lu depuis le body JSON`,L=`/**
|
|
87
|
+
* ${w} \u2014 pure business logic, no HTTP awareness.
|
|
86
88
|
*
|
|
87
89
|
* Owns its Zod \`input\` / \`output\` schemas (declared as \`static\` members, the
|
|
88
90
|
* single source of truth shared with \`routes.ts\`) and runs the logic in
|
|
@@ -91,11 +93,11 @@ Examples:
|
|
|
91
93
|
*/
|
|
92
94
|
|
|
93
95
|
import { z } from "zod";
|
|
94
|
-
import { UseCase } from "${
|
|
95
|
-
import type { Services } from "${
|
|
96
|
+
import { UseCase } from "${D}";
|
|
97
|
+
import type { Services } from "${h}";
|
|
96
98
|
|
|
97
99
|
const input = z.object({
|
|
98
|
-
${
|
|
100
|
+
${b}
|
|
99
101
|
example: z.string(),
|
|
100
102
|
});
|
|
101
103
|
|
|
@@ -103,7 +105,7 @@ const output = z.object({
|
|
|
103
105
|
id: z.string(),
|
|
104
106
|
});
|
|
105
107
|
|
|
106
|
-
export class ${
|
|
108
|
+
export class ${w} extends UseCase<typeof input, typeof output, Services> {
|
|
107
109
|
static readonly input = input;
|
|
108
110
|
static readonly output = output;
|
|
109
111
|
|
|
@@ -114,22 +116,22 @@ export class ${y} extends UseCase<typeof input, typeof output, Services> {
|
|
|
114
116
|
return { id: payload.example };
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
|
-
`,
|
|
118
|
-
source: "query",`:"",
|
|
119
|
-
`:"",
|
|
120
|
-
import { useCaseRoute } from "${
|
|
121
|
-
${
|
|
119
|
+
`,l=a==="get"?`
|
|
120
|
+
source: "query",`:"",_=p?`import { ${w} } from "./${$}.js";
|
|
121
|
+
`:"",U=m(t["apis-import"])??Pe(S,g),se=p?`import { defineRoutes } from "${D}";
|
|
122
|
+
import { useCaseRoute } from "${U}";
|
|
123
|
+
${_}
|
|
122
124
|
export default defineRoutes([
|
|
123
|
-
useCaseRoute(${
|
|
125
|
+
useCaseRoute(${w}, {
|
|
124
126
|
api: "${d}",
|
|
125
|
-
method: "${a}",${
|
|
126
|
-
summary: "TODO: ${
|
|
127
|
+
method: "${a}",${l}
|
|
128
|
+
summary: "TODO: ${i}",
|
|
127
129
|
tags: ["${c}"],
|
|
128
130
|
}),
|
|
129
131
|
]);
|
|
130
132
|
`:`import { z } from "zod";
|
|
131
|
-
import { defineRoutes } from "${
|
|
132
|
-
import { defineRoute } from "${
|
|
133
|
+
import { defineRoutes } from "${D}";
|
|
134
|
+
import { defineRoute } from "${U}";
|
|
133
135
|
|
|
134
136
|
export default defineRoutes([
|
|
135
137
|
defineRoute({
|
|
@@ -137,7 +139,7 @@ export default defineRoutes([
|
|
|
137
139
|
method: "${a}",
|
|
138
140
|
|
|
139
141
|
input: z.object({
|
|
140
|
-
${
|
|
142
|
+
${b}
|
|
141
143
|
example: z.string(),
|
|
142
144
|
}),
|
|
143
145
|
|
|
@@ -145,7 +147,7 @@ export default defineRoutes([
|
|
|
145
147
|
id: z.string(),
|
|
146
148
|
}),
|
|
147
149
|
|
|
148
|
-
summary: "TODO: ${
|
|
150
|
+
summary: "TODO: ${i}",
|
|
149
151
|
tags: ["${c}"],
|
|
150
152
|
|
|
151
153
|
handler: async ({ input }) => {
|
|
@@ -154,44 +156,52 @@ export default defineRoutes([
|
|
|
154
156
|
},
|
|
155
157
|
}),
|
|
156
158
|
]);
|
|
157
|
-
`,z=[],G=[],
|
|
158
|
-
import type { Services } from "${
|
|
159
|
-
import { ${
|
|
159
|
+
`,z=[],G=[],N=(v,re)=>{if(existsSync(v)&&!y){G.push(v);return}writeFileSync(v,re,"utf8"),z.push(v);};if(N(A,se),p&&N(R,L),p&&P){let v=`import { describe, it, expect } from "vitest";
|
|
160
|
+
import type { Services } from "${h}";
|
|
161
|
+
import { ${w} } from "./${$}.js";
|
|
160
162
|
|
|
161
|
-
describe("${
|
|
163
|
+
describe("${w}", () => {
|
|
162
164
|
it("returns a response shaped like the output schema", async () => {
|
|
163
165
|
// TODO: replace with real mocks for the services the useCase consumes.
|
|
164
166
|
const services = {} as unknown as Services;
|
|
165
167
|
|
|
166
|
-
const useCase = new ${
|
|
168
|
+
const useCase = new ${w}(services);
|
|
167
169
|
const result = await useCase.execute({ example: "hello" });
|
|
168
170
|
expect(result).toMatchObject({ id: expect.any(String) });
|
|
169
171
|
});
|
|
170
172
|
|
|
171
173
|
// TODO: add error-path tests, repository mocks, etc.
|
|
172
174
|
});
|
|
173
|
-
`;
|
|
174
|
-
[frs] reminder: run "frs gen --root ${
|
|
175
|
-
basePath: "${
|
|
175
|
+
`;N(k,v);}for(let v of z)console.log(`[frs] wrote ${v}`);for(let v of G)console.log(`[frs] skipped ${v} (use --force to overwrite)`);console.log(`
|
|
176
|
+
[frs] reminder: run "frs gen --root ${n}" to refresh the manifest.`);}finally{r.close();}}async function we(e){let t=e.yes===true,s=te(t);try{let r=e.force===!0,o=m(e.root);o||(o=(await s.ask("Domain root","src/domains")).trim()||"src/domains");let i=m(e["apis-file"]);i||(i=(await s.ask("apis.ts location","src/apis.ts")).trim()||"src/apis.ts");let c=m(e["services-file"]);if(!c){let l=i.replace(/apis\.ts$/,"services.ts")||"src/services.ts";c=(await s.ask("services.ts location",l)).trim()||l;}let n=m(e.apis);n||(n=(await s.ask("API tags (comma-separated)","v1")).trim()||"v1");let a=n.split(",").map(l=>l.trim()).filter(Boolean);a.length===0&&(console.error("[frs] at least one API tag is required"),process.exit(2));let d=m(e["base-path"]),f=resolve(process.cwd(),o),p=resolve(process.cwd(),i),P=resolve(process.cwd(),c),y=resolve(f,"__generated__"),S=resolve(y,"routes.ts"),g=[],A=[],$=(l,_)=>{if(mkdirSync(dirname(l),{recursive:!0}),existsSync(l)&&!r){A.push(l);return}writeFileSync(l,_,"utf8"),g.push(l);},R=a.map(l=>{let _=d??`/${l}`;return ` ${l}: {
|
|
177
|
+
basePath: "${_}",
|
|
176
178
|
openapi: {
|
|
177
|
-
info: { title: "${
|
|
179
|
+
info: { title: "${l.toUpperCase()} API", version: "1.0.0", description: "" },
|
|
178
180
|
},
|
|
181
|
+
// Built-in error mapping. Extend BaseErrorHandler to map your own
|
|
182
|
+
// domain errors, then swap it in here (per API).
|
|
183
|
+
errorHandler: new BaseErrorHandler(),
|
|
184
|
+
// Structured console logger. Extend BaseLogger (override \`write\`) to
|
|
185
|
+
// route to your sink, then swap it in here (per API).
|
|
186
|
+
logger: new BaseLogger(),
|
|
179
187
|
verbose: process.env["NODE_ENV"] !== "production",
|
|
180
188
|
},`}).join(`
|
|
181
|
-
`),
|
|
182
|
-
import { services } from "${
|
|
189
|
+
`),k=`import { BaseErrorHandler, BaseLogger, createApiRegistry } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
190
|
+
import { services } from "${I(dirname(p),P)}";
|
|
183
191
|
|
|
184
192
|
/**
|
|
185
193
|
* Single source of truth for every API exposed by this project.
|
|
186
194
|
* Add per-API middlewares, interceptors, OpenAPI metadata here.
|
|
187
195
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
196
|
+
* Per-API resources injected into every handler / interceptor / error-handler
|
|
197
|
+
* context (override them per API above):
|
|
198
|
+
* - \`services\` \u2014 shared DI container (\`services.ctx.c\` = current request);
|
|
199
|
+
* - \`errorHandler\` \u2014 maps thrown errors \u2192 HTTP (extend \`BaseErrorHandler\`);
|
|
200
|
+
* - \`logger\` \u2014 structured logging (extend \`BaseLogger\`).
|
|
191
201
|
*/
|
|
192
202
|
export const apis = createApiRegistry(
|
|
193
203
|
{
|
|
194
|
-
${
|
|
204
|
+
${R}
|
|
195
205
|
},
|
|
196
206
|
{ services },
|
|
197
207
|
);
|
|
@@ -199,7 +209,7 @@ ${k}
|
|
|
199
209
|
/** Typed helpers used inside every route file. */
|
|
200
210
|
export const defineRoute = apis.defineRoute;
|
|
201
211
|
export const useCaseRoute = apis.useCaseRoute;
|
|
202
|
-
|
|
212
|
+
`;$(p,k),$(P,`import { createServices } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
203
213
|
|
|
204
214
|
/**
|
|
205
215
|
* Global DI container \u2014 declare every singleton (repositories, SDK
|
|
@@ -232,22 +242,22 @@ export const services = createServices({
|
|
|
232
242
|
|
|
233
243
|
/** Convenience type \u2014 \`function fn(svc: Services) { ... }\`. */
|
|
234
244
|
export type Services = typeof services;
|
|
235
|
-
`);let
|
|
245
|
+
`);let w=`// AUTO-GENERATED by frs \u2014 do not edit.
|
|
236
246
|
// Run \`frs gen --root ${o}\` to refresh.
|
|
237
247
|
|
|
238
248
|
import type { AnyRouteDef } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
239
249
|
|
|
240
250
|
export const routes: AnyRouteDef[] = [];
|
|
241
|
-
|
|
251
|
+
`;$(S,w);let D=he({root:o,apisFile:i,servicesFile:c,apis:a});g.push(D);let h=I(dirname(p),p),b=I(dirname(p),S),L=a.length===1?`export const { ${a[0]} } = apis.toFunctions(routes, onRequest, {`:`export const { ${a.join(", ")} } = apis.toFunctions(routes, onRequest, {`;for(let l of g)console.log(`[frs] wrote ${l}`);for(let l of A)console.log(`[frs] skipped ${l} (use --force to overwrite)`);console.log(`
|
|
242
252
|
Next steps:
|
|
243
253
|
|
|
244
254
|
1. Wire the registry in your Functions entrypoint (e.g. src/index.ts):
|
|
245
255
|
|
|
246
256
|
import { onRequest } from "firebase-functions/v2/https";
|
|
247
257
|
import { apis } from "${h}";
|
|
248
|
-
import { routes } from "${
|
|
258
|
+
import { routes } from "${b}";
|
|
249
259
|
|
|
250
|
-
${
|
|
260
|
+
${L}
|
|
251
261
|
defaults: { region: "us-central1", invoker: "public" },
|
|
252
262
|
});
|
|
253
263
|
|
|
@@ -258,10 +268,10 @@ Next steps:
|
|
|
258
268
|
3. Refresh the manifest before each build:
|
|
259
269
|
|
|
260
270
|
frs gen --root ${o}
|
|
261
|
-
`);}finally{s.close();}}function
|
|
271
|
+
`);}finally{s.close();}}function I(e,t){let s=relative(e,t).replace(/\\/g,"/");return s=s.replace(/\.ts$/,".js"),s.startsWith(".")||(s=`./${s}`),s}async function xe(e,t,s){e!=="service"&&(console.error(`[frs] unknown "add" target: ${e??"(missing)"} \u2014 supported: service`),process.exit(2)),t||(console.error("[frs] service name is required: frs add service <name>"),process.exit(2));let r=s.force===true,o=T(),c=[m(s["services-file"]),o.servicesFile,"src/services.ts","services.ts"].filter(h=>typeof h=="string"&&h.length>0),n;for(let h of c){let b=resolve(process.cwd(),h);if(existsSync(b)){n=b;break}}if(!n){let h=c.map(b=>resolve(process.cwd(),b)).join(`
|
|
262
272
|
`);console.error(`[frs] services file not found. Tried:
|
|
263
273
|
${h}
|
|
264
|
-
Run \`frs init\` first or pass --services-file <path>.`),process.exit(2);}let a=m(s["services-dir"])??o.servicesDir??resolve(dirname(
|
|
274
|
+
Run \`frs init\` first or pass --services-file <path>.`),process.exit(2);}let a=m(s["services-dir"])??o.servicesDir??resolve(dirname(n),"services"),d=resolve(process.cwd(),a);mkdirSync(d,{recursive:true});let f=`${t.charAt(0).toUpperCase()}${t.slice(1)}Service`,p=resolve(d,`${t}.ts`),P=`import type { RequestContext } from "@lpdjs/firestore-repo-service/servers/hono";
|
|
265
275
|
|
|
266
276
|
/**
|
|
267
277
|
* ${f} \u2014 generated by \`frs add service ${t}\`.
|
|
@@ -290,9 +300,9 @@ export class ${f} {
|
|
|
290
300
|
return \`hello from ${t} \u2014 user=\${this.ctx.maybeC?.get("user")?.id ?? "anonymous"}\`;
|
|
291
301
|
}
|
|
292
302
|
}
|
|
293
|
-
`;existsSync(
|
|
294
|
-
`),
|
|
295
|
-
`),
|
|
296
|
-
`+
|
|
297
|
-
`),
|
|
303
|
+
`;existsSync(p)&&!r?console.log(`[frs] skipped ${p} (use --force to overwrite)`):(writeFileSync(p,P,"utf8"),console.log(`[frs] wrote ${p}`));let y=readFileSync(n,"utf8"),S=I(dirname(n),p),g=`import { ${f} } from "${S}";`,A=` ${t}: ({ ctx }) => new ${f}(ctx),`;if(y.includes(g)){console.log(`[frs] services.ts already registers "${t}" \u2014 skipping.`);return}let $=y.split(`
|
|
304
|
+
`),R=-1;for(let h=0;h<$.length;h++)/^import\s/.test($[h])&&(R=h);R>=0?$.splice(R+1,0,g):$.unshift(g);let k=$.join(`
|
|
305
|
+
`),F=k.match(/createServices\s*\(\s*\{/);if(!F){console.error(`[frs] could not find \`createServices({\` in ${n} \u2014 register "${t}" manually.`);return}let w=F.index+F[0].length,D=k.slice(0,w)+`
|
|
306
|
+
`+A+k.slice(w);writeFileSync(n,D,"utf8"),console.log(`[frs] updated ${n} (+ ${t})`);}function Pe(e,t){let s=["apis.ts","apis.js","api.ts","api.js"],r=[e,dirname(e),dirname(dirname(e))];for(let o of r)for(let i of s){let c=resolve(o,i);if(existsSync(c)){let n=relative(t,c).replace(/\\/g,"/");return n=n.replace(/\.ts$/,".js").replace(/\.js$/,".js"),n.startsWith(".")||(n=`./${n}`),n}}return "../../../../apis.js"}function Se(e,t){let s=["services.ts","services.js"],r=[e,dirname(e),dirname(dirname(e))];for(let o of r)for(let i of s){let c=resolve(o,i);if(existsSync(c)){let n=relative(t,c).replace(/\\/g,"/");return n=n.replace(/\.ts$/,".js"),n.startsWith(".")||(n=`./${n}`),n}}return "../../../../services.js"}async function be(){let e=process.argv.slice(2),{command:t,flags:s}=ve(e);switch(t){case "init":await we(s);return;case "gen":await ye(s);return;case "new":await $e(e[1],s);return;case "add":await xe(e[1],e[2],s);return;case "help":case "--help":case "-h":Z();return;default:console.error(`[frs] unknown command: ${t}
|
|
307
|
+
`),Z(),process.exit(2);}}be().catch(e=>{console.error(e),process.exit(1);});//# sourceMappingURL=cli.js.map
|
|
298
308
|
//# sourceMappingURL=cli.js.map
|