@mybuildex/pkg 0.9.0 → 0.10.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/README.md CHANGED
@@ -51,6 +51,24 @@ createAuthMiddleware({
51
51
  createTenantMiddleware({getTenantClient, defaultClient, strictTenant: true});
52
52
  ```
53
53
 
54
+ ## 0.10.0 — bundle da API e boot antecipado
55
+
56
+ Tudo opt-in: API que não usa não muda. Medido no piloto da `v3_peoples` (04/10/2026): boot do código da API de 4,8 s para 1,45 s e primeiro GET em instância fria de 4,6 s para 0,79 s.
57
+
58
+ - **`mybuildex-bundle`** (comando de build) — empacota `dist/index.js` em `dist/bundle.js` com o esbuild. No Cloud Run cada `require()` custa ~3 ms e o boot lia ~1.280 arquivos; o bundle troca isso por um arquivo. O `@fastify/autoload` não funciona dentro de um bundle (ele varre a pasta de módulos em runtime), então o comando faz a mesma varredura no build e gera um plugin estático equivalente (`dist/autoload-static.js`), com as regras do autoload 5.x. Depois **confere** que as rotas do bundle são idênticas às do autoload (compara o swagger das duas entradas) e quebra o build se divergirem. `src/app.ts` não muda; dev e teste seguem no autoload de verdade.
59
+
60
+ ```jsonc
61
+ // package.json da API
62
+ "main": "dist/bundle.js",
63
+ "scripts": { "build": "tsc && mybuildex-bundle", "gcp-build": "npm run build" },
64
+ "devDependencies": { "esbuild": "^0.28.2" },
65
+ // opcional: libs que a API só carrega sob demanda ficam fora do bundle
66
+ "mybuildex": { "bundle": { "external": ["exceljs", "pdf-lib"] } }
67
+ ```
68
+
69
+ Ficam sempre fora do bundle (vêm do `node_modules`): `@fastify/swagger-ui`, `firebase-admin`, `@google-cloud/storage`, `redis`, `@google/genai` e o WASM do Prisma. A verificação sobe a API com banco falso (porta morta) e precisa do que ela precisa para carregar (ex.: `firebase.json`); `verifyEnv` passa env a mais, `--no-verify` pula. Não cobre `autoHooks` (o comando lança). Stack trace passa a apontar para o `bundle.js`.
70
+ - **`createGcfHandler({server, eagerBoot: true})`** — dispara o `ready()` logo depois da carga do módulo, em vez de esperar a primeira request: a instância que sobe antes do tráfego chega pronta. As garantias do wrapper valem igual (sem `unhandledRejection`; falha de boot vira 503 + saída na request seguinte).
71
+
54
72
  ## 0.9.0 — cold start e tamanho do bundle
55
73
 
56
74
  **Muda a instalação** (⚠️): a CLI `prisma` saiu das `dependencies` do pacote.
@@ -0,0 +1,10 @@
1
+ export interface AutoloadEntry {
2
+ /** Caminho absoluto do arquivo. */
3
+ file: string;
4
+ /** Prefixo de diretório que o autoload daria (undefined na raiz). */
5
+ prefix: string | undefined;
6
+ }
7
+ /** A mesma varredura do findPlugins do autoload (dirNameRoutePrefix: true, sem autoHooks). */
8
+ export declare function findAutoloadEntries(dir: string, prefix?: string, found?: AutoloadEntry[]): AutoloadEntry[];
9
+ /** Código-fonte (CommonJS) do plugin estático; `fromDir` é a pasta onde ele será gravado. */
10
+ export declare function renderAutoloadStatic(entries: AutoloadEntry[], fromDir: string): string;
@@ -0,0 +1,155 @@
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.findAutoloadEntries = findAutoloadEntries;
7
+ exports.renderAutoloadStatic = renderAutoloadStatic;
8
+ // Equivalente ESTÁTICO do @fastify/autoload, gerado em tempo de build.
9
+ //
10
+ // O autoload varre a pasta de módulos em RUNTIME (readdir) e dá require() em
11
+ // cada arquivo pelo caminho. Dentro de um bundle essa pasta não é lida — os
12
+ // módulos estão embutidos — então o autoload não acharia rota nenhuma (ou
13
+ // acharia os arquivos soltos e carregaria tudo de novo, fora do bundle).
14
+ //
15
+ // Aqui a MESMA varredura roda no build (findAutoloadEntries) e vira um plugin
16
+ // com um require() fixo por arquivo (renderAutoloadStatic), que o bundler
17
+ // embute. As regras de "é plugin?", autoPrefix, prefixOverride, autoConfig e
18
+ // dependências são as do autoload 5.x (loadPlugin/registerPlugin), copiadas.
19
+ // Não cobre autoHooks: a varredura lança se encontrar um.
20
+ const node_fs_1 = __importDefault(require("node:fs"));
21
+ const node_path_1 = __importDefault(require("node:path"));
22
+ // Padrões copiados dos defaults do @fastify/autoload 5.x.
23
+ const scriptPattern = /(?:(?:^.?|\.[^d]|[^.]d|[^.][^d])\.ts|\.js|\.cjs|\.mjs|\.cts|\.mts)$/iu;
24
+ const indexPattern = /^index(?:\.ts|\.js|\.cjs|\.mjs|\.cts|\.mts)$/iu;
25
+ const autoHooksPattern = /^[_.]?auto_?hooks(?:\.ts|\.js|\.cjs|\.mjs|\.cts|\.mts)$/iu;
26
+ /** A mesma varredura do findPlugins do autoload (dirNameRoutePrefix: true, sem autoHooks). */
27
+ function findAutoloadEntries(dir, prefix, found = []) {
28
+ const list = node_fs_1.default.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
29
+ if (list.some((d) => autoHooksPattern.test(d.name))) {
30
+ throw new Error(`autohooks em ${dir}: o autoload estático não cobre autoHooks`);
31
+ }
32
+ const indexDirent = list.find((d) => indexPattern.test(d.name));
33
+ if (indexDirent) {
34
+ found.push({ file: node_path_1.default.join(dir, indexDirent.name), prefix });
35
+ if (!list.some((d) => d.isDirectory()))
36
+ return found;
37
+ }
38
+ if (list.some((d) => d.name === "package.json") && !indexDirent) {
39
+ throw new Error(`@fastify/autoload não importaria o plugin em '${dir}' (package.json sem index)`);
40
+ }
41
+ for (const dirent of list) {
42
+ const file = node_path_1.default.join(dir, dirent.name);
43
+ if (dirent.isDirectory()) {
44
+ findAutoloadEntries(file, (prefix ? `${prefix}/` : "/") + dirent.name, found);
45
+ continue;
46
+ }
47
+ if (indexDirent)
48
+ continue;
49
+ if (dirent.isFile() && scriptPattern.test(dirent.name))
50
+ found.push({ file, prefix });
51
+ }
52
+ return found;
53
+ }
54
+ /** Código-fonte (CommonJS) do plugin estático; `fromDir` é a pasta onde ele será gravado. */
55
+ function renderAutoloadStatic(entries, fromDir) {
56
+ const rel = (file) => {
57
+ const r = node_path_1.default.relative(fromDir, file).split(node_path_1.default.sep).join("/");
58
+ return r.startsWith(".") ? r : `./${r}`;
59
+ };
60
+ const lines = entries.map((e) => ` {file: ${JSON.stringify(rel(e.file))}, prefix: ${JSON.stringify(e.prefix)}, load: () => require(${JSON.stringify(rel(e.file))})},`);
61
+ return `// GERADO pelo mybuildex-bundle (@mybuildex/pkg) — NÃO EDITAR.
62
+ // Equivalente estático do @fastify/autoload: lista fixa de arquivos (um
63
+ // require por arquivo, que o bundler embute) com as regras do autoload 5.x.
64
+ "use strict";
65
+
66
+ const entries = [
67
+ ${lines.join("\n")}
68
+ ];
69
+
70
+ function isRouteObject(input) {
71
+ return !!input && Object.prototype.toString.call(input) === "[object Object]" &&
72
+ Object.prototype.hasOwnProperty.call(input, "method");
73
+ }
74
+
75
+ function isPluginOrModule(input) {
76
+ const inputType = Object.prototype.toString.call(input);
77
+ if (/\\[object (?:AsyncFunction|Function|Module)\\]/u.test(inputType)) return true;
78
+ if (Object.prototype.hasOwnProperty.call(input, "default")) return isPluginOrModule(input.default);
79
+ return isRouteObject(input);
80
+ }
81
+
82
+ function wrapRoutes(content) {
83
+ if (isRouteObject(content)) {
84
+ return async function (fastify) {
85
+ fastify.route(content);
86
+ };
87
+ }
88
+ return content;
89
+ }
90
+
91
+ function loadPlugin(entry, overrideConfig) {
92
+ const content = entry.load();
93
+ if (isPluginOrModule(content) === false) return undefined;
94
+
95
+ const plugin = wrapRoutes(content.default || content);
96
+ const pluginConfig = (content.default && content.default.autoConfig) || content.autoConfig || {};
97
+ const pluginOptions = Object.assign({}, pluginConfig, overrideConfig);
98
+ const pluginMeta = plugin[Symbol.for("plugin-meta")] || {};
99
+
100
+ if (plugin.autoload === false || content.autoload === false) return undefined;
101
+ if (plugin.autoConfig !== undefined) plugin.autoConfig = undefined;
102
+
103
+ pluginOptions.prefix = (pluginOptions.prefix && pluginOptions.prefix.endsWith("/")) ? pluginOptions.prefix.slice(0, -1) : pluginOptions.prefix;
104
+ const prefixOverride = plugin.prefixOverride !== undefined ? plugin.prefixOverride : content.prefixOverride !== undefined ? content.prefixOverride : undefined;
105
+ const prefix = (plugin.autoPrefix !== undefined ? plugin.autoPrefix : content.autoPrefix !== undefined ? content.autoPrefix : undefined) || entry.prefix;
106
+ if (prefixOverride !== undefined) {
107
+ pluginOptions.prefix = prefixOverride;
108
+ } else if (prefix) {
109
+ pluginOptions.prefix = (pluginOptions.prefix || "") + prefix.replace(/\\/+/gu, "/");
110
+ }
111
+
112
+ return {
113
+ plugin,
114
+ filename: entry.file,
115
+ name: pluginMeta.name || entry.file,
116
+ dependencies: pluginMeta.dependencies,
117
+ options: pluginOptions,
118
+ registered: false,
119
+ };
120
+ }
121
+
122
+ function registerPlugin(fastify, meta, allPlugins, parentPlugins = {}) {
123
+ const {plugin, name, options, dependencies = []} = meta;
124
+ if (parentPlugins[name]) throw new Error("Cyclic dependency");
125
+ if (meta.registered) return;
126
+ parentPlugins[name] = true;
127
+ for (const dependency of dependencies) {
128
+ if (allPlugins[dependency]) registerPlugin(fastify, allPlugins[dependency], allPlugins, {...parentPlugins});
129
+ }
130
+ fastify.register(plugin, options);
131
+ meta.registered = true;
132
+ }
133
+
134
+ const autoloadStatic = async function autoloadStatic(fastify, options) {
135
+ const pluginsMeta = {};
136
+ for (const entry of entries) {
137
+ const meta = loadPlugin(entry, options && options.options);
138
+ if (meta) pluginsMeta[meta.name] = meta;
139
+ }
140
+ const metas = Object.values(pluginsMeta);
141
+ for (const entry of entries) {
142
+ const meta = metas.find((m) => m.filename === entry.file);
143
+ if (meta) registerPlugin(fastify, meta, pluginsMeta);
144
+ }
145
+ };
146
+
147
+ // não cria contexto novo, igual ao autoload (e ao fastify-plugin)
148
+ autoloadStatic[Symbol.for("skip-override")] = true;
149
+
150
+ module.exports = autoloadStatic;
151
+ module.exports.fastifyAutoload = autoloadStatic;
152
+ module.exports.default = autoloadStatic;
153
+ `;
154
+ }
155
+ //# sourceMappingURL=autoload-static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoload-static.js","sourceRoot":"","sources":["../../src/build/autoload-static.ts"],"names":[],"mappings":";;;;;AA4BA,kDAuBC;AAGD,oDAoGC;AA1JD,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,yEAAyE;AACzE,EAAE;AACF,8EAA8E;AAC9E,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,0DAA0D;AAC1D,sDAAyB;AACzB,0DAA6B;AAS7B,0DAA0D;AAC1D,MAAM,aAAa,GAAG,uEAAuE,CAAC;AAC9F,MAAM,YAAY,GAAG,gDAAgD,CAAC;AACtE,MAAM,gBAAgB,GAAG,2DAA2D,CAAC;AAErF,8FAA8F;AAC9F,SAAgB,mBAAmB,CAAC,GAAW,EAAE,MAAe,EAAE,QAAyB,EAAE;IACzF,MAAM,IAAI,GAAG,iBAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAC,aAAa,EAAE,IAAI,EAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,2CAA2C,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,IAAI,WAAW,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,mBAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;IACzD,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,iDAAiD,GAAG,4BAA4B,CAAC,CAAC;IACtG,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,mBAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,mBAAmB,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC9E,SAAS;QACb,CAAC;QACD,IAAI,WAAW;YAAE,SAAS;QAC1B,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,MAAM,EAAC,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,6FAA6F;AAC7F,SAAgB,oBAAoB,CAAC,OAAwB,EAAE,OAAe;IAC1E,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE;QACzB,MAAM,CAAC,GAAG,mBAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,mBAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5C,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5B,cAAc,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7I,OAAO;;;;;;EAMT,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsFjB,CAAC;AACF,CAAC"}
@@ -0,0 +1,28 @@
1
+ /** Pacotes que ficam FORA do bundle (continuam vindo do node_modules em runtime). */
2
+ export declare const DEFAULT_BUNDLE_EXTERNAL: readonly string[];
3
+ export interface BundleOptions {
4
+ /** Raiz da API (default: process.cwd()). */
5
+ root?: string;
6
+ /** Pasta do compilado, relativa à raiz (default: "dist"). */
7
+ distDir?: string;
8
+ /** Entrada, relativa a distDir (default: "index.js"). */
9
+ entry?: string;
10
+ /** Saída, relativa a distDir (default: "bundle.js"). */
11
+ outfile?: string;
12
+ /** Pasta que o @fastify/autoload varre, relativa a distDir (default: "modules"). */
13
+ modulesDir?: string;
14
+ /** Pacotes a mais fora do bundle — tipicamente os que a API só carrega sob
15
+ * demanda (`await import("exceljs")`). Somam aos DEFAULT_BUNDLE_EXTERNAL. */
16
+ external?: string[];
17
+ /** `target` do esbuild (default: "node22"). */
18
+ target?: string;
19
+ /** Log (default: console.log). */
20
+ log?: (message: string) => void;
21
+ }
22
+ export interface BundleResult {
23
+ outfile: string;
24
+ bytes: number;
25
+ inputs: number;
26
+ autoloadEntries: number;
27
+ }
28
+ export declare function bundleApi(options?: BundleOptions): Promise<BundleResult>;
@@ -0,0 +1,84 @@
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.DEFAULT_BUNDLE_EXTERNAL = void 0;
7
+ exports.bundleApi = bundleApi;
8
+ // Empacota a API num arquivo só para o cold start.
9
+ //
10
+ // POR QUÊ: no Cloud Run cada require() custa ~3 ms (medido na v3_peoples em
11
+ // 04/10/2026: 79% do boot era require de ~1.280 arquivos; com o bundle o boot
12
+ // caiu de 4,8 s para 1,45 s). Um arquivo só troca mais de mil leituras por uma.
13
+ //
14
+ // O @fastify/autoload não funciona dentro de um bundle; aqui ele é trocado
15
+ // (alias) pelo equivalente estático gerado em ./autoload-static. O código da
16
+ // API não muda: dev e teste seguem no autoload de verdade.
17
+ //
18
+ // Só ferramenta de BUILD: nada daqui é exportado pelo index do pacote, e o
19
+ // esbuild é dependência de desenvolvimento da API (peer opcional daqui).
20
+ const node_fs_1 = __importDefault(require("node:fs"));
21
+ const node_path_1 = __importDefault(require("node:path"));
22
+ const autoload_static_1 = require("./autoload-static");
23
+ /** Pacotes que ficam FORA do bundle (continuam vindo do node_modules em runtime). */
24
+ exports.DEFAULT_BUNDLE_EXTERNAL = [
25
+ // serve arquivos estáticos da própria pasta (path.join(__dirname, "static"))
26
+ "@fastify/swagger-ui",
27
+ // carrega os serviços por require() sob demanda; no boot são poucos arquivos
28
+ "firebase-admin",
29
+ // só carregam quando usados: dentro do bundle só aumentariam o arquivo que
30
+ // o Node lê em todo cold start
31
+ "@google-cloud/storage",
32
+ "redis",
33
+ "@google/genai",
34
+ // WASM do query compiler do Prisma (MBs em base64): o client gerado só o
35
+ // importa na primeira query
36
+ "@prisma/client/runtime/*.wasm-base64.js",
37
+ ];
38
+ function loadEsbuild(root) {
39
+ try {
40
+ /* eslint-disable @typescript-eslint/no-var-requires */
41
+ return require(require.resolve("esbuild", { paths: [root, __dirname] }));
42
+ }
43
+ catch {
44
+ throw new Error("mybuildex-bundle precisa do esbuild na API: npm i -D esbuild");
45
+ }
46
+ }
47
+ async function bundleApi(options = {}) {
48
+ const root = node_path_1.default.resolve(options.root ?? process.cwd());
49
+ const dist = node_path_1.default.join(root, options.distDir ?? "dist");
50
+ const entry = node_path_1.default.join(dist, options.entry ?? "index.js");
51
+ const outfile = node_path_1.default.join(dist, options.outfile ?? "bundle.js");
52
+ const modulesDir = node_path_1.default.join(dist, options.modulesDir ?? "modules");
53
+ const shim = node_path_1.default.join(dist, "autoload-static.js");
54
+ const log = options.log ?? ((message) => console.log(message));
55
+ if (!node_fs_1.default.existsSync(entry))
56
+ throw new Error(`${node_path_1.default.relative(root, entry)} não existe: rode o tsc antes`);
57
+ const alias = {};
58
+ let autoloadEntries = 0;
59
+ if (node_fs_1.default.existsSync(modulesDir)) {
60
+ const entries = (0, autoload_static_1.findAutoloadEntries)(modulesDir);
61
+ node_fs_1.default.writeFileSync(shim, (0, autoload_static_1.renderAutoloadStatic)(entries, dist));
62
+ alias["@fastify/autoload"] = shim;
63
+ autoloadEntries = entries.length;
64
+ }
65
+ const esbuild = loadEsbuild(root);
66
+ const result = await esbuild.build({
67
+ entryPoints: [entry],
68
+ outfile,
69
+ bundle: true,
70
+ platform: "node",
71
+ target: options.target ?? "node22",
72
+ format: "cjs",
73
+ external: [...exports.DEFAULT_BUNDLE_EXTERNAL, ...(options.external ?? [])],
74
+ alias,
75
+ legalComments: "none",
76
+ logLevel: "warning",
77
+ metafile: true,
78
+ });
79
+ const bytes = node_fs_1.default.statSync(outfile).size;
80
+ const inputs = Object.keys(result.metafile.inputs).length;
81
+ log(`bundle: ${node_path_1.default.relative(root, outfile)} ${(bytes / 1e6).toFixed(1)} MB (${inputs} arquivos embutidos, ${autoloadEntries} arquivos de módulo no autoload estático)`);
82
+ return { outfile, bytes, inputs, autoloadEntries };
83
+ }
84
+ //# sourceMappingURL=bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/build/bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ // mybuildex-bundle — empacota a API (dist/index.js -> dist/bundle.js) e confere
8
+ // que as rotas do bundle são idênticas às do @fastify/autoload.
9
+ //
10
+ // Uso na API (package.json):
11
+ // "main": "dist/bundle.js",
12
+ // "scripts": { "build": "tsc && mybuildex-bundle" }
13
+ //
14
+ // Configuração opcional, no package.json da API:
15
+ // "mybuildex": { "bundle": {
16
+ // "external": ["exceljs", "pdf-lib"], // libs que a API só carrega sob demanda
17
+ // "verifyEnv": {"MINHA_ENV": "x"}, // env a mais para a API carregar na verificação
18
+ // "swaggerPath": "/documentation/json",
19
+ // "entry": "index.js", "outfile": "bundle.js", "modulesDir": "modules", "distDir": "dist"
20
+ // } }
21
+ //
22
+ // Flags: --no-verify (só empacota). MYBUILDEX_BUNDLE_VERIFY=0 faz o mesmo.
23
+ const node_fs_1 = __importDefault(require("node:fs"));
24
+ const node_path_1 = __importDefault(require("node:path"));
25
+ const bundle_1 = require("./bundle");
26
+ const verify_bundle_1 = require("./verify-bundle");
27
+ async function main() {
28
+ const root = process.cwd();
29
+ const pkgFile = node_path_1.default.join(root, "package.json");
30
+ const pkg = node_fs_1.default.existsSync(pkgFile) ? JSON.parse(node_fs_1.default.readFileSync(pkgFile, "utf8")) : {};
31
+ const config = pkg?.mybuildex?.bundle ?? {};
32
+ const skipVerify = process.argv.includes("--no-verify") || process.env.MYBUILDEX_BUNDLE_VERIFY === "0";
33
+ await (0, bundle_1.bundleApi)({
34
+ root,
35
+ distDir: config.distDir,
36
+ entry: config.entry,
37
+ outfile: config.outfile,
38
+ modulesDir: config.modulesDir,
39
+ external: config.external,
40
+ target: config.target,
41
+ });
42
+ if (skipVerify) {
43
+ console.log("verify-bundle: pulado (--no-verify)");
44
+ return;
45
+ }
46
+ const { routes } = (0, verify_bundle_1.verifyBundle)({
47
+ root,
48
+ distDir: config.distDir,
49
+ entry: config.entry,
50
+ outfile: config.outfile,
51
+ swaggerPath: config.swaggerPath,
52
+ env: config.verifyEnv,
53
+ });
54
+ console.log(`verify-bundle: ok — ${routes} rotas idênticas no autoload e no bundle`);
55
+ }
56
+ main().catch((error) => {
57
+ console.error(error?.message ?? error);
58
+ process.exit(1);
59
+ });
60
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/build/cli.ts"],"names":[],"mappings":";;;;;;AACA,gFAAgF;AAChF,gEAAgE;AAChE,EAAE;AACF,6BAA6B;AAC7B,8BAA8B;AAC9B,sDAAsD;AACtD,EAAE;AACF,iDAAiD;AACjD,+BAA+B;AAC/B,0FAA0F;AAC1F,kGAAkG;AAClG,8CAA8C;AAC9C,gGAAgG;AAChG,QAAQ;AACR,EAAE;AACF,2EAA2E;AAC3E,sDAAyB;AACzB,0DAA6B;AAC7B,qCAAmC;AACnC,mDAA6C;AAE7C,KAAK,UAAU,IAAI;IACf,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,iBAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,MAAM,MAAM,GAAG,GAAG,EAAE,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,CAAC;IAEvG,MAAM,IAAA,kBAAS,EAAC;QACZ,IAAI;QACJ,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;KACxB,CAAC,CAAC;IAEH,IAAI,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,OAAO;IACX,CAAC;IACD,MAAM,EAAC,MAAM,EAAC,GAAG,IAAA,4BAAY,EAAC;QAC1B,IAAI;QACJ,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,GAAG,EAAE,MAAM,CAAC,SAAS;KACxB,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,uBAAuB,MAAM,0CAA0C,CAAC,CAAC;AACzF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ export interface VerifyBundleOptions {
2
+ /** Raiz da API (default: process.cwd()). */
3
+ root?: string;
4
+ /** Pasta do compilado, relativa à raiz (default: "dist"). */
5
+ distDir?: string;
6
+ /** Entrada original, relativa a distDir (default: "index.js"). */
7
+ entry?: string;
8
+ /** Bundle, relativo a distDir (default: "bundle.js"). */
9
+ outfile?: string;
10
+ /** Caminho do JSON do swagger (default: "/documentation/json"). */
11
+ swaggerPath?: string;
12
+ /** Variáveis de ambiente a mais para a API carregar na verificação. */
13
+ env?: Record<string, string>;
14
+ /** Tempo máximo por entrada, ms (default: 120000). */
15
+ timeoutMs?: number;
16
+ }
17
+ export interface VerifyBundleResult {
18
+ routes: number;
19
+ }
20
+ export declare function verifyBundle(options?: VerifyBundleOptions): VerifyBundleResult;
@@ -0,0 +1,116 @@
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.verifyBundle = verifyBundle;
7
+ // Confere que o bundle (autoload estático) expõe EXATAMENTE as mesmas rotas
8
+ // que a entrada original com o @fastify/autoload de verdade.
9
+ //
10
+ // Sobe cada entrada num processo próprio, pede o JSON do swagger (ele lista
11
+ // toda rota registrada, com método, caminho e schema) e compara. Também
12
+ // garante que o bundle não leu arquivo solto do dist/ — se leu, o autoload
13
+ // estático não está valendo. Qualquer diferença lança: um bundle com rota
14
+ // faltando não pode chegar no deploy.
15
+ //
16
+ // Não toca em banco: DATABASE_URL aponta para uma porta morta e só o swagger
17
+ // é chamado. Precisa do que a API precisa para CARREGAR (ex.: firebase.json).
18
+ const node_child_process_1 = require("node:child_process");
19
+ const node_path_1 = __importDefault(require("node:path"));
20
+ // Roda dentro do processo filho: carrega a entrada e devolve o swagger.
21
+ const WORKER = `
22
+ const http = require("node:http");
23
+ const path = require("node:path");
24
+ const entry = path.resolve(process.argv[1]);
25
+ const swaggerPath = process.argv[2];
26
+ const silent = () => {};
27
+ console.log = silent; console.warn = silent; console.error = silent;
28
+ process.on("unhandledRejection", silent);
29
+ const mod = require(entry);
30
+ const handler = typeof mod === "function" ? mod : (mod.default || Object.values(mod).find((v) => typeof v === "function"));
31
+ const server = http.createServer((req, res) => handler(req, res));
32
+ server.listen(0, "127.0.0.1", () => {
33
+ http.get({host: "127.0.0.1", port: server.address().port, path: swaggerPath}, (res) => {
34
+ let body = "";
35
+ res.on("data", (c) => body += c);
36
+ res.on("end", () => {
37
+ const loose = Object.keys(require.cache).filter((f) => f.startsWith(path.dirname(entry) + path.sep) && f !== entry);
38
+ // sai só depois de o pipe aceitar tudo: exit() direto truncaria o JSON
39
+ process.stdout.write(JSON.stringify({status: res.statusCode, body, loose: loose.length}), () => process.exit(0));
40
+ });
41
+ }).on("error", (e) => { process.stderr.write(String(e)); process.exit(1); });
42
+ });
43
+ `;
44
+ // JSON com as chaves ordenadas: a ORDEM das rotas no swagger não é estável
45
+ // nem entre duas execuções do próprio autoload (ele varre as pastas em
46
+ // paralelo), então só o conteúdo é comparado.
47
+ function canonical(value) {
48
+ if (Array.isArray(value))
49
+ return value.map(canonical);
50
+ if (value && typeof value === "object") {
51
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])]));
52
+ }
53
+ return value;
54
+ }
55
+ function operations(doc) {
56
+ const list = [];
57
+ for (const [route, methods] of Object.entries(doc?.paths ?? {})) {
58
+ for (const method of Object.keys(methods))
59
+ list.push(`${method.toUpperCase()} ${route}`);
60
+ }
61
+ return list.sort();
62
+ }
63
+ function verifyBundle(options = {}) {
64
+ const root = node_path_1.default.resolve(options.root ?? process.cwd());
65
+ const dist = node_path_1.default.join(root, options.distDir ?? "dist");
66
+ const swaggerPath = options.swaggerPath ?? "/documentation/json";
67
+ const swaggerOf = (file) => {
68
+ const label = node_path_1.default.relative(root, file);
69
+ const result = (0, node_child_process_1.spawnSync)(process.execPath, ["-e", WORKER, file, swaggerPath], {
70
+ cwd: root,
71
+ encoding: "utf8",
72
+ timeout: options.timeoutMs ?? 120000,
73
+ maxBuffer: 64 * 1024 * 1024,
74
+ env: {
75
+ ...process.env,
76
+ DATABASE_URL: "mysql://verify:verify@127.0.0.1:1/verify_school_1?connection_limit=1",
77
+ HTTP: "https",
78
+ DOMAIN: "verify.local/api",
79
+ PROJECTID: process.env.PROJECTID || "verify",
80
+ APIKEY: process.env.APIKEY || "verify",
81
+ STORAGEBUCKET: process.env.STORAGEBUCKET || "verify.appspot.com",
82
+ REDIS: "",
83
+ ...(options.env ?? {}),
84
+ },
85
+ });
86
+ if (result.status !== 0 || !result.stdout) {
87
+ throw new Error(`verify-bundle: ${label} não subiu (status ${result.status}): ${(result.stderr || "").slice(0, 2000)}`);
88
+ }
89
+ const out = JSON.parse(result.stdout);
90
+ if (out.status !== 200)
91
+ throw new Error(`verify-bundle: ${label} respondeu ${out.status} em ${swaggerPath}`);
92
+ return { doc: JSON.parse(out.body), loose: out.loose };
93
+ };
94
+ const original = swaggerOf(node_path_1.default.join(dist, options.entry ?? "index.js"));
95
+ const bundle = swaggerOf(node_path_1.default.join(dist, options.outfile ?? "bundle.js"));
96
+ const a = operations(original.doc);
97
+ const b = operations(bundle.doc);
98
+ const missing = a.filter((op) => !b.includes(op));
99
+ const extra = b.filter((op) => !a.includes(op));
100
+ const problems = [];
101
+ if (a.length === 0)
102
+ problems.push("a entrada original não registrou rota nenhuma (o build está quebrado antes do bundle)");
103
+ if (missing.length)
104
+ problems.push(`rotas que o autoload registra e o bundle NÃO: ${missing.slice(0, 20).join(", ")}`);
105
+ if (extra.length)
106
+ problems.push(`rotas só no bundle: ${extra.slice(0, 20).join(", ")}`);
107
+ if (!missing.length && !extra.length && JSON.stringify(canonical(original.doc)) !== JSON.stringify(canonical(bundle.doc))) {
108
+ problems.push("mesmas rotas, mas o swagger (schemas/tags) difere entre o autoload e o bundle");
109
+ }
110
+ if (bundle.loose > 0)
111
+ problems.push(`o bundle carregou ${bundle.loose} arquivo(s) soltos de ${options.distDir ?? "dist"}/ — o autoload estático não está valendo`);
112
+ if (problems.length)
113
+ throw new Error("verify-bundle: FALHOU\n - " + problems.join("\n - "));
114
+ return { routes: a.length };
115
+ }
116
+ //# sourceMappingURL=verify-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-bundle.js","sourceRoot":"","sources":["../../src/build/verify-bundle.ts"],"names":[],"mappings":"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"}
@@ -23,5 +23,14 @@ export interface GcfHandlerOptions {
23
23
  exitDelayMs?: number;
24
24
  /** Função de saída (default: process.exit) — injetável para teste */
25
25
  exit?: (code: number) => void;
26
+ /** Boot antecipado (default: false). Dispara o ready() logo depois da
27
+ * carga do módulo, em vez de esperar a primeira request: a instância que
28
+ * sobe antes do tráfego (rollout, autoscaling) chega pronta, e o boot roda
29
+ * na janela de CPU extra do startup do Cloud Run. Medido na v3_peoples em
30
+ * 04/10/2026: eram 2,6 s de boot DENTRO da primeira request. As três
31
+ * garantias do wrapper valem igual — a rejeição do boot antecipado já
32
+ * nasce com handler, e uma falha continua virando 503 + saída na request
33
+ * seguinte. */
34
+ eagerBoot?: boolean;
26
35
  }
27
36
  export declare function createGcfHandler(options: GcfHandlerOptions): (req: any, res: any) => Promise<void>;
@@ -35,7 +35,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
35
35
  exports.createGcfHandler = createGcfHandler;
36
36
  const plugin_1 = require("../cors/plugin");
37
37
  function createGcfHandler(options) {
38
- const { server, origin = "*", methods = plugin_1.DEFAULT_CORS.methods.join(","), headers = "*", maxAge = "3600", handlePreflight = true, bootFailureStatus = 503, bootFailureMessage = "Serviço iniciando, tente novamente em instantes", exitOnBootFailure = true, exitDelayMs = 2000, exit = (code) => process.exit(code), } = options;
38
+ const { server, origin = "*", methods = plugin_1.DEFAULT_CORS.methods.join(","), headers = "*", maxAge = "3600", handlePreflight = true, bootFailureStatus = 503, bootFailureMessage = "Serviço iniciando, tente novamente em instantes", exitOnBootFailure = true, exitDelayMs = 2000, exit = (code) => process.exit(code), eagerBoot = false, } = options;
39
39
  // null = ainda não bootou (ou o boot anterior falhou e será re-tentado)
40
40
  let readyPromise = null;
41
41
  function getReady() {
@@ -49,6 +49,12 @@ function createGcfHandler(options) {
49
49
  }
50
50
  return readyPromise;
51
51
  }
52
+ // nextTick: só depois que o módulo da API terminou de carregar — registros
53
+ // feitos DEPOIS do createGcfHandler (plugins, content type parsers) ainda
54
+ // entram. O catch vazio é o que impede o unhandledRejection aqui: sem
55
+ // request esperando, ninguém mais trataria a falha.
56
+ if (eagerBoot)
57
+ process.nextTick(() => getReady().catch(() => { }));
52
58
  let exitScheduled = false;
53
59
  // Sai só DEPOIS que a resposta foi escrita no socket: process.exit() no
54
60
  // mesmo tick cortaria o 503 e o browser voltaria a ver "erro de CORS".
@@ -1 +1 @@
1
- {"version":3,"file":"gcf-handler.js","sourceRoot":"","sources":["../../src/http/gcf-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gcf-handler.js","sourceRoot":"","sources":["../../src/http/gcf-handler.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mybuildex/pkg",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Infra comum das APIs MyBuilder: multitenancy Prisma (pool único), middleware de tenant e CORS",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,6 +21,9 @@
21
21
  ],
22
22
  "main": "dist/index.js",
23
23
  "types": "dist/index.d.ts",
24
+ "bin": {
25
+ "mybuildex-bundle": "dist/build/cli.js"
26
+ },
24
27
  "files": [
25
28
  "dist"
26
29
  ],
@@ -45,17 +48,23 @@
45
48
  "redis": "^5.12.1"
46
49
  },
47
50
  "peerDependencies": {
51
+ "esbuild": ">=0.20",
48
52
  "fastify": ">=4"
49
53
  },
50
54
  "peerDependenciesMeta": {
55
+ "esbuild": {
56
+ "optional": true
57
+ },
51
58
  "fastify": {
52
59
  "optional": true
53
60
  }
54
61
  },
55
62
  "devDependencies": {
63
+ "@fastify/autoload": "^5.0.0",
56
64
  "@google-cloud/functions-framework": "^3.5.1",
57
65
  "@types/express": "^4.17.25",
58
66
  "@types/node": "^22.0.0",
67
+ "esbuild": "^0.28.2",
59
68
  "express": "^4.22.2",
60
69
  "fastify": "^4.26.2",
61
70
  "typescript": "^5.5.0",