@mybuildex/pkg 0.3.3 → 0.3.4
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/http/gcf-handler.d.ts +20 -0
- package/dist/http/gcf-handler.js +81 -0
- package/dist/http/gcf-handler.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface GcfHandlerOptions {
|
|
2
|
+
/** Instância Fastify já com os plugins registrados (server.register(app)) */
|
|
3
|
+
server: any;
|
|
4
|
+
/** Access-Control-Allow-Origin do fallback (default: "*") */
|
|
5
|
+
origin?: string;
|
|
6
|
+
/** Access-Control-Allow-Methods do fallback (default: métodos do DEFAULT_CORS) */
|
|
7
|
+
methods?: string;
|
|
8
|
+
/** Access-Control-Allow-Headers do fallback (default: "*") */
|
|
9
|
+
headers?: string;
|
|
10
|
+
/** Access-Control-Max-Age do preflight, em segundos (default: "3600") */
|
|
11
|
+
maxAge?: string;
|
|
12
|
+
/** Responder OPTIONS no wrapper, sem depender do boot (default: true).
|
|
13
|
+
* Garante que o preflight funcione mesmo com o Fastify ainda subindo. */
|
|
14
|
+
handlePreflight?: boolean;
|
|
15
|
+
/** Status quando o boot falha (default: 503 — é transitório e re-tentável) */
|
|
16
|
+
bootFailureStatus?: number;
|
|
17
|
+
/** Mensagem quando o boot falha (nunca expõe o erro cru ao cliente) */
|
|
18
|
+
bootFailureMessage?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function createGcfHandler(options: GcfHandlerOptions): (req: any, res: any) => Promise<void>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Wrapper do handler das Cloud Functions. Nasceu do incidente de 01/09/2026
|
|
3
|
+
// ("ora erro 500, ora erro de CORS" em todas as APIs).
|
|
4
|
+
//
|
|
5
|
+
// CADEIA REAL DA FALHA (confirmada nos logs e reproduzida em laboratório):
|
|
6
|
+
// 1. As APIs faziam `const readyPromise = server.ready()` no CARREGAMENTO DO
|
|
7
|
+
// MÓDULO, sem anexar handler de rejeição no mesmo tick.
|
|
8
|
+
// 2. Num cold start estrangulado (0,58 vCPU, concurrency=1, maxScale=3) o boot
|
|
9
|
+
// estoura o pluginTimeout — default do Fastify v4 é apenas 10s.
|
|
10
|
+
// 3. A rejeição sem handler vira unhandledRejection. O functions-framework
|
|
11
|
+
// escuta esse evento e chama sendCrashResponse -> process.exit(16).
|
|
12
|
+
// 4. A instância MORRE no meio da resposta. O browser não recebe headers nem
|
|
13
|
+
// status: reporta "erro de CORS"/ERR_FAILED — mascarando o 500 real.
|
|
14
|
+
// Daí o sintoma intermitente "ora 500, ora CORS".
|
|
15
|
+
//
|
|
16
|
+
// TRÊS GARANTIAS DESTE WRAPPER:
|
|
17
|
+
// 1. O boot nunca gera unhandledRejection — o .catch() é anexado no mesmo tick
|
|
18
|
+
// em que a promise é criada, e o ready() só dispara na primeira request.
|
|
19
|
+
// 2. Falha de boot NÃO é cacheada: a próxima request re-tenta. Sem o reset, a
|
|
20
|
+
// instância servia a MESMA promise rejeitada até morrer de velhice.
|
|
21
|
+
// 3. CORS em TODA resposta, inclusive 404 e falha de boot. O @fastify/cors é
|
|
22
|
+
// registrado dentro do plugin `app`, que é encapsulado — então o handler de
|
|
23
|
+
// 404 da RAIZ respondia sem Access-Control-Allow-Origin (verificado em
|
|
24
|
+
// produção e reproduzido byte a byte).
|
|
25
|
+
//
|
|
26
|
+
// Os headers vão no `res` CRU antes de entregar ao Fastify. `setHeader`
|
|
27
|
+
// SOBRESCREVE, então o @fastify/cors não duplica o header nas rotas normais.
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.createGcfHandler = createGcfHandler;
|
|
30
|
+
const plugin_1 = require("../cors/plugin");
|
|
31
|
+
function createGcfHandler(options) {
|
|
32
|
+
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", } = options;
|
|
33
|
+
// null = ainda não bootou (ou o boot anterior falhou e será re-tentado)
|
|
34
|
+
let readyPromise = null;
|
|
35
|
+
function getReady() {
|
|
36
|
+
if (!readyPromise) {
|
|
37
|
+
// o .catch() nasce junto com a promise: nunca há um tick em que a
|
|
38
|
+
// rejeição fique sem handler (era isso que matava a instância).
|
|
39
|
+
readyPromise = server.ready().catch((e) => {
|
|
40
|
+
readyPromise = null;
|
|
41
|
+
throw e;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return readyPromise;
|
|
45
|
+
}
|
|
46
|
+
function applyCors(res) {
|
|
47
|
+
if (typeof res?.setHeader !== "function")
|
|
48
|
+
return;
|
|
49
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
50
|
+
res.setHeader("Access-Control-Allow-Methods", methods);
|
|
51
|
+
res.setHeader("Access-Control-Allow-Headers", headers);
|
|
52
|
+
res.setHeader("Access-Control-Max-Age", maxAge);
|
|
53
|
+
// origin fixo específico varia por origem: sem Vary o cache do browser
|
|
54
|
+
// (e o CDN) serviria a resposta de uma origem para outra.
|
|
55
|
+
if (origin !== "*")
|
|
56
|
+
res.setHeader("Vary", "Origin");
|
|
57
|
+
}
|
|
58
|
+
return async function gcfHandler(req, res) {
|
|
59
|
+
applyCors(res);
|
|
60
|
+
if (handlePreflight && req?.method === "OPTIONS") {
|
|
61
|
+
res.statusCode = 204;
|
|
62
|
+
res.end();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
await getReady();
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
// Log completo para o Cloud Logging; resposta sanitizada e COM CORS,
|
|
70
|
+
// para o browser mostrar o erro real em vez de "erro de CORS".
|
|
71
|
+
console.error("[gcf] boot do Fastify falhou:", e);
|
|
72
|
+
res.statusCode = bootFailureStatus;
|
|
73
|
+
res.setHeader?.("Content-Type", "application/json; charset=utf-8");
|
|
74
|
+
res.setHeader?.("Retry-After", "1");
|
|
75
|
+
res.end(JSON.stringify({ statusCode: bootFailureStatus, message: bootFailureMessage }));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
server.server.emit("request", req, res);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=gcf-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcf-handler.js","sourceRoot":"","sources":["../../src/http/gcf-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,4 +20,6 @@ export { initFirebaseAdmin, getTenantAuth, getDefaultAuth, getBucket } from "./f
|
|
|
20
20
|
export type { FirebaseAdminOptions } from "./firebase/admin";
|
|
21
21
|
export { createErrorHandler, isInternalError } from "./http/error-handler";
|
|
22
22
|
export type { ErrorHandlerOptions } from "./http/error-handler";
|
|
23
|
+
export { createGcfHandler } from "./http/gcf-handler";
|
|
24
|
+
export type { GcfHandlerOptions } from "./http/gcf-handler";
|
|
23
25
|
export { GeminiHelp, EMBED_DIM, GEMINI_MODEL, EMBED_MODEL } from "./ai/gemini";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EMBED_MODEL = exports.GEMINI_MODEL = exports.EMBED_DIM = exports.GeminiHelp = exports.isInternalError = exports.createErrorHandler = exports.getBucket = exports.getDefaultAuth = exports.getTenantAuth = exports.initFirebaseAdmin = exports.getFirebaseStorage = exports.getFirebaseClientApp = exports.createFirebaseClientPlugin = exports.createSwaggerPlugin = exports.DEFAULT_CORS = exports.createCorsPlugin = exports.withTenantSchema = exports.createMultitenantPrismaMariaDb = exports.createMultitenantPrisma = exports.createAuthMiddleware = exports.createTenantMiddleware = exports.resolveTenantId = void 0;
|
|
3
|
+
exports.EMBED_MODEL = exports.GEMINI_MODEL = exports.EMBED_DIM = exports.GeminiHelp = exports.createGcfHandler = exports.isInternalError = exports.createErrorHandler = exports.getBucket = exports.getDefaultAuth = exports.getTenantAuth = exports.initFirebaseAdmin = exports.getFirebaseStorage = exports.getFirebaseClientApp = exports.createFirebaseClientPlugin = exports.createSwaggerPlugin = exports.DEFAULT_CORS = exports.createCorsPlugin = exports.withTenantSchema = exports.createMultitenantPrismaMariaDb = exports.createMultitenantPrisma = exports.createAuthMiddleware = exports.createTenantMiddleware = exports.resolveTenantId = void 0;
|
|
4
4
|
var resolve_1 = require("./tenant/resolve");
|
|
5
5
|
Object.defineProperty(exports, "resolveTenantId", { enumerable: true, get: function () { return resolve_1.resolveTenantId; } });
|
|
6
6
|
var middleware_1 = require("./tenant/middleware");
|
|
@@ -30,6 +30,8 @@ Object.defineProperty(exports, "getBucket", { enumerable: true, get: function ()
|
|
|
30
30
|
var error_handler_1 = require("./http/error-handler");
|
|
31
31
|
Object.defineProperty(exports, "createErrorHandler", { enumerable: true, get: function () { return error_handler_1.createErrorHandler; } });
|
|
32
32
|
Object.defineProperty(exports, "isInternalError", { enumerable: true, get: function () { return error_handler_1.isInternalError; } });
|
|
33
|
+
var gcf_handler_1 = require("./http/gcf-handler");
|
|
34
|
+
Object.defineProperty(exports, "createGcfHandler", { enumerable: true, get: function () { return gcf_handler_1.createGcfHandler; } });
|
|
33
35
|
var gemini_1 = require("./ai/gemini");
|
|
34
36
|
Object.defineProperty(exports, "GeminiHelp", { enumerable: true, get: function () { return gemini_1.GeminiHelp; } });
|
|
35
37
|
Object.defineProperty(exports, "EMBED_DIM", { enumerable: true, get: function () { return gemini_1.EMBED_DIM; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,4CAAiD;AAAzC,0GAAA,eAAe,OAAA;AAGvB,kDAA2D;AAAnD,oHAAA,sBAAsB,OAAA;AAG9B,sCAAmD;AAA3C,4GAAA,oBAAoB,OAAA;AAG5B,oDAA6D;AAArD,sHAAA,uBAAuB,OAAA;AAC/B,4DAAwE;AAAhE,iIAAA,8BAA8B,OAAA;AAGtC,kDAAqD;AAA7C,8GAAA,gBAAgB,OAAA;AAUxB,wCAA6D;AAArD,0GAAA,gBAAgB,OAAA;AAAE,sGAAA,YAAY,OAAA;AAGtC,2CAAqD;AAA7C,6GAAA,mBAAmB,OAAA;AAG3B,0DAA8G;AAAtG,2HAAA,0BAA0B,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAG5E,0CAA6F;AAArF,0GAAA,iBAAiB,OAAA;AAAE,sGAAA,aAAa,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,kGAAA,SAAS,OAAA;AAGnE,sDAAyE;AAAjE,mHAAA,kBAAkB,OAAA;AAAE,gHAAA,eAAe,OAAA;AAG3C,sCAA6E;AAArE,oGAAA,UAAU,OAAA;AAAE,mGAAA,SAAS,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,qGAAA,WAAW,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,4CAAiD;AAAzC,0GAAA,eAAe,OAAA;AAGvB,kDAA2D;AAAnD,oHAAA,sBAAsB,OAAA;AAG9B,sCAAmD;AAA3C,4GAAA,oBAAoB,OAAA;AAG5B,oDAA6D;AAArD,sHAAA,uBAAuB,OAAA;AAC/B,4DAAwE;AAAhE,iIAAA,8BAA8B,OAAA;AAGtC,kDAAqD;AAA7C,8GAAA,gBAAgB,OAAA;AAUxB,wCAA6D;AAArD,0GAAA,gBAAgB,OAAA;AAAE,sGAAA,YAAY,OAAA;AAGtC,2CAAqD;AAA7C,6GAAA,mBAAmB,OAAA;AAG3B,0DAA8G;AAAtG,2HAAA,0BAA0B,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAG5E,0CAA6F;AAArF,0GAAA,iBAAiB,OAAA;AAAE,sGAAA,aAAa,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,kGAAA,SAAS,OAAA;AAGnE,sDAAyE;AAAjE,mHAAA,kBAAkB,OAAA;AAAE,gHAAA,eAAe,OAAA;AAG3C,kDAAoD;AAA5C,+GAAA,gBAAgB,OAAA;AAGxB,sCAA6E;AAArE,oGAAA,UAAU,OAAA;AAAE,mGAAA,SAAS,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,qGAAA,WAAW,OAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mybuildex/pkg",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Infra comum das APIs MyBuilder: multitenancy Prisma (pool único), middleware de tenant e CORS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"typescript": "^5.5.0",
|
|
57
57
|
"vitest": "^3.0.0"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|