@meuecommerce/frete-adapter-node 0.1.0 → 0.2.1

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.
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCorreiosHttpClient = createCorreiosHttpClient;
4
+ /**
5
+ * Node implementation of the `CorreiosClient` port using global `fetch`.
6
+ *
7
+ * Ports the retry/backoff behavior from the Velo backend:
8
+ * - `auth-methods.js` -> `authenticate` (up to 5 tries, fail fast on 4xx)
9
+ * - `estimate-methods.js` -> `getPrice`/`getTime` (timeout + 2 retries, backoff)
10
+ *
11
+ * The only host dependency is `fetch`, injectable for tests.
12
+ */
13
+ const frete_1 = require("@meuecommerce/frete");
14
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
15
+ function base64(input) {
16
+ return Buffer.from(input, "utf-8").toString("base64");
17
+ }
18
+ function createCorreiosHttpClient(options = {}) {
19
+ const { fetch = globalThis.fetch, baseUrl = frete_1.correiosApiUrl, timeoutMs = 20000, estimateRetries = 2, estimateRetryDelayMs = 500, authRetries = 5, authRetryDelayMs = 1000, logger = console, } = options;
20
+ if (typeof fetch !== "function") {
21
+ throw new Error("createCorreiosHttpClient: no fetch available (Node 18+ or pass options.fetch)");
22
+ }
23
+ /** Run a fetch with a hard timeout + exponential-backoff retry. */
24
+ async function fetchWithRetry(url, init) {
25
+ let lastError;
26
+ for (let attempt = 0; attempt < estimateRetries; attempt++) {
27
+ const controller = new AbortController();
28
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
29
+ try {
30
+ return await fetch(url, { ...init, signal: controller.signal });
31
+ }
32
+ catch (error) {
33
+ lastError = error;
34
+ if (attempt === estimateRetries - 1)
35
+ break;
36
+ await sleep(estimateRetryDelayMs * Math.pow(2, attempt));
37
+ }
38
+ finally {
39
+ clearTimeout(timer);
40
+ }
41
+ }
42
+ throw lastError;
43
+ }
44
+ async function estimate(path, payload, token, tag) {
45
+ const url = `${baseUrl}${path}`;
46
+ const init = {
47
+ method: "POST",
48
+ headers: {
49
+ "Content-Type": "application/json",
50
+ Accept: "application/json",
51
+ Authorization: `Bearer ${token}`,
52
+ },
53
+ body: JSON.stringify(payload),
54
+ };
55
+ const response = await fetchWithRetry(url, init);
56
+ if (response.status === 401) {
57
+ throw { status: 401, message: "unauthorized" };
58
+ }
59
+ if (response.status !== 200 && response.status !== 206) {
60
+ // Correios returns a structured error body on bad requests; the domain
61
+ // layer filters these out by `txErro`, so pass it through unchanged.
62
+ const json = await response.json().catch(() => ({}));
63
+ logger.warn(`[${tag}] invalid response. status=${response.status} body=${JSON.stringify(json)}`);
64
+ return json;
65
+ }
66
+ return (await response.json());
67
+ }
68
+ return {
69
+ async authenticate(credentials) {
70
+ const url = `${baseUrl}/token/v1/autentica/cartaopostagem`;
71
+ let lastError;
72
+ for (let attempt = 0; attempt < authRetries; attempt++) {
73
+ try {
74
+ const response = await fetch(url, {
75
+ method: "POST",
76
+ headers: {
77
+ "Content-Type": "application/json",
78
+ accept: "application/json",
79
+ Authorization: `Basic ${base64(`${credentials.user}:${credentials.apiKey}`)}`,
80
+ },
81
+ body: JSON.stringify({ numero: credentials.postcard }),
82
+ });
83
+ // 4xx come from bad credentials/contract, not transient failures —
84
+ // fail fast instead of burning retries (and seconds) on them.
85
+ if (response.status >= 400 && response.status < 500) {
86
+ throw {
87
+ status: response.status,
88
+ message: response.status === 401
89
+ ? "Erro de autenticação nos correios."
90
+ : "Não foi possível autenticar o contrato nos Correios.",
91
+ };
92
+ }
93
+ return (await response.json());
94
+ }
95
+ catch (error) {
96
+ const status = error?.status;
97
+ if (typeof status === "number" && status >= 400 && status < 500)
98
+ throw error;
99
+ lastError = error;
100
+ if (attempt < authRetries - 1) {
101
+ await sleep(authRetryDelayMs);
102
+ }
103
+ else {
104
+ // apiKey intentionally omitted from logs — it is a secret.
105
+ logger.error("[correios.authenticate]:", error, credentials.user, credentials.postcard);
106
+ throw error;
107
+ }
108
+ }
109
+ }
110
+ throw lastError;
111
+ },
112
+ getPrice(payload, token) {
113
+ return estimate("/preco/v1/nacional", payload, token, "correios.getPrice");
114
+ },
115
+ getTime(payload, token) {
116
+ return estimate("/prazo/v1/nacional", payload, token, "correios.getTime");
117
+ },
118
+ };
119
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EnvSecretStore = void 0;
4
+ class EnvSecretStore {
5
+ env;
6
+ prefix;
7
+ constructor(options = {}) {
8
+ this.env = options.env ?? process.env;
9
+ this.prefix = options.prefix ?? "MEUFRETE_";
10
+ }
11
+ key(name) {
12
+ return this.prefix + name.replace(/[^a-zA-Z0-9]+/g, "_").toUpperCase();
13
+ }
14
+ async getSecret(name) {
15
+ const value = this.env[this.key(name)];
16
+ return value == null || value === "" ? null : value;
17
+ }
18
+ }
19
+ exports.EnvSecretStore = EnvSecretStore;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemorySettingsStore = void 0;
4
+ class InMemorySettingsStore {
5
+ defaults;
6
+ merchants = new Map();
7
+ constructor(options) {
8
+ this.defaults = options.defaults;
9
+ for (const [id, settings] of Object.entries(options.merchants ?? {})) {
10
+ this.merchants.set(id, settings);
11
+ }
12
+ }
13
+ /** Add or replace a merchant's settings. */
14
+ set(instanceId, settings) {
15
+ this.merchants.set(instanceId, settings);
16
+ }
17
+ async getMerchantSettings(instanceId) {
18
+ return this.merchants.get(instanceId) ?? null;
19
+ }
20
+ async getDefaultSettings() {
21
+ return this.defaults;
22
+ }
23
+ }
24
+ exports.InMemorySettingsStore = InMemorySettingsStore;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOpenAIDimensionEstimator = exports.EnvSecretStore = exports.InMemorySettingsStore = exports.createCorreiosHttpClient = void 0;
4
+ /**
5
+ * @meuecommerce/frete-adapter-node — Node implementations of @meuecommerce/frete ports.
6
+ *
7
+ * Wraps global `fetch` so the shipping core can run outside Velo: on the
8
+ * Shopify/Fly deployment today, and the MCP server next.
9
+ */
10
+ var correiosHttpClient_js_1 = require("./correiosHttpClient.js");
11
+ Object.defineProperty(exports, "createCorreiosHttpClient", { enumerable: true, get: function () { return correiosHttpClient_js_1.createCorreiosHttpClient; } });
12
+ var inMemorySettingsStore_js_1 = require("./inMemorySettingsStore.js");
13
+ Object.defineProperty(exports, "InMemorySettingsStore", { enumerable: true, get: function () { return inMemorySettingsStore_js_1.InMemorySettingsStore; } });
14
+ var envSecretStore_js_1 = require("./envSecretStore.js");
15
+ Object.defineProperty(exports, "EnvSecretStore", { enumerable: true, get: function () { return envSecretStore_js_1.EnvSecretStore; } });
16
+ var openAIDimensionEstimator_js_1 = require("./openAIDimensionEstimator.js");
17
+ Object.defineProperty(exports, "createOpenAIDimensionEstimator", { enumerable: true, get: function () { return openAIDimensionEstimator_js_1.createOpenAIDimensionEstimator; } });
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOpenAIDimensionEstimator = createOpenAIDimensionEstimator;
4
+ const ESTIMATE_DIMENSIONS_TOOL = {
5
+ type: "function",
6
+ function: {
7
+ name: "estimate_dimensions",
8
+ description: "Estima dimensões de produtos PRESERVANDO os nomes exatos fornecidos",
9
+ parameters: {
10
+ type: "object",
11
+ properties: {
12
+ products: {
13
+ type: "array",
14
+ items: {
15
+ type: "object",
16
+ properties: {
17
+ name: { type: "string" },
18
+ length: { type: "number", description: "Comprimento em cm" },
19
+ width: { type: "number", description: "Largura em cm" },
20
+ height: { type: "number", description: "Altura em cm" },
21
+ },
22
+ required: ["name", "length", "width", "height"],
23
+ },
24
+ },
25
+ },
26
+ required: ["products"],
27
+ },
28
+ },
29
+ };
30
+ function largestBox(boxes) {
31
+ return boxes.reduce((largest, current) => current.width * current.height * current.length > largest.width * largest.height * largest.length ? current : largest);
32
+ }
33
+ function createPrompt(products, boxes, samples) {
34
+ const box = largestBox(boxes);
35
+ const maxWidth = Math.floor(box.width * 0.8);
36
+ const maxHeight = Math.floor(box.height * 0.8);
37
+ const maxLength = Math.floor(box.length * 0.8);
38
+ const samplesSection = samples.length > 0
39
+ ? `\n🌟 EXEMPLOS DO USUÁRIO (PRIORIDADE MÁXIMA):\n${samples.map((s, i) => `${i + 1}. ${s}`).join("\n")}\n\n` +
40
+ `📋 INSTRUÇÕES:\n1. Para cada produto, procure um exemplo similar acima\n2. Match exato → use dimensões EXATAS\n3. Similar → ajuste\n4. Sem match → conhecimento geral\n`
41
+ : `\n📋 INSTRUÇÕES (SEM EXEMPLOS):\n1. Considere a embalagem comercial típica brasileira\n2. Use conhecimento sobre produtos similares\n3. Ajuste baseado no peso\n`;
42
+ return (`Você é um especialista em dimensões de produtos brasileiros para e-commerce.\n\n` +
43
+ `🚨 LIMITES FÍSICOS OBRIGATÓRIOS:\n- Largura máxima: ${maxWidth}cm\n- Altura máxima: ${maxHeight}cm\n- Comprimento máximo: ${maxLength}cm\n(Maior caixa: ${box.name ?? "—"})\n\n` +
44
+ `📦 PRODUTOS PARA ESTIMAR:\n${products.map((p, i) => `${i + 1}. ${p.name} - ${p.quantity}x (${p.weight}kg cada)`).join("\n")}\n` +
45
+ samplesSection +
46
+ `\n⚠️ REGRAS: respeite os limites; preserve os nomes EXATOS; retorne length×width×height em cm.\n` +
47
+ `Responda usando a função 'estimate_dimensions'.`);
48
+ }
49
+ /** Apply the largest-box physical limits + a minimum-volume floor to AI output. */
50
+ function validateAndOptimize(aiResult, products, boxes) {
51
+ const box = largestBox(boxes);
52
+ const maxWidth = Math.floor(box.width * 0.8);
53
+ const maxHeight = Math.floor(box.height * 0.8);
54
+ const maxLength = Math.floor(box.length * 0.8);
55
+ const optimized = aiResult.products.map((product) => {
56
+ let { length, width, height } = product;
57
+ if (length > maxLength)
58
+ length = maxLength;
59
+ if (width > maxWidth)
60
+ width = maxWidth;
61
+ if (height > maxHeight)
62
+ height = maxHeight;
63
+ const volume = length * width * height;
64
+ const original = products.find((p) => p.name === product.name);
65
+ const minVolume = (original?.weight || 0.1) * 500;
66
+ if (volume > 0 && volume < minVolume) {
67
+ const scale = Math.cbrt(minVolume / volume);
68
+ length = Math.min(maxLength, length * scale);
69
+ width = Math.min(maxWidth, width * scale);
70
+ height = Math.min(maxHeight, height * scale);
71
+ }
72
+ return {
73
+ name: product.name,
74
+ length: Math.round(length * 10) / 10,
75
+ width: Math.round(width * 10) / 10,
76
+ height: Math.round(height * 10) / 10,
77
+ };
78
+ });
79
+ return { products: optimized };
80
+ }
81
+ function createOpenAIDimensionEstimator(options) {
82
+ const { apiKey, fetch = globalThis.fetch, model = "gpt-4o-mini", temperature = 0.1, apiUrl = "https://api.openai.com/v1/chat/completions", timeoutMs = 20000, } = options;
83
+ if (!apiKey)
84
+ throw new Error("createOpenAIDimensionEstimator: apiKey is required");
85
+ if (typeof fetch !== "function")
86
+ throw new Error("createOpenAIDimensionEstimator: no fetch available");
87
+ return {
88
+ async estimate(products, boxes, productSamples = []) {
89
+ if (!boxes.length)
90
+ throw new Error("estimate: no boxes provided");
91
+ const body = {
92
+ model,
93
+ messages: [
94
+ {
95
+ role: "system",
96
+ content: "Você é um especialista em dimensões de produtos. Use os exemplos personalizados como prioridade máxima quando o nome bater.",
97
+ },
98
+ { role: "user", content: createPrompt(products, boxes, productSamples) },
99
+ ],
100
+ tools: [ESTIMATE_DIMENSIONS_TOOL],
101
+ tool_choice: { type: "function", function: { name: "estimate_dimensions" } },
102
+ temperature,
103
+ };
104
+ const controller = new AbortController();
105
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
106
+ let response;
107
+ try {
108
+ response = await fetch(apiUrl, {
109
+ method: "POST",
110
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
111
+ body: JSON.stringify(body),
112
+ signal: controller.signal,
113
+ });
114
+ }
115
+ finally {
116
+ clearTimeout(timer);
117
+ }
118
+ if (!response.ok)
119
+ throw new Error(`OpenAI API Error: ${response.status} ${response.statusText}`);
120
+ const json = (await response.json());
121
+ if (json.error)
122
+ throw new Error(`OpenAI Error: ${json.error.message}`);
123
+ const args = json.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments;
124
+ if (!args)
125
+ throw new Error("IA não conseguiu estimar dimensões dos produtos");
126
+ const parsed = JSON.parse(args);
127
+ return validateAndOptimize(parsed, products, boxes);
128
+ },
129
+ };
130
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
package/dist/index.d.ts CHANGED
@@ -10,3 +10,5 @@ export { InMemorySettingsStore } from "./inMemorySettingsStore.js";
10
10
  export type { InMemorySettingsStoreOptions } from "./inMemorySettingsStore.js";
11
11
  export { EnvSecretStore } from "./envSecretStore.js";
12
12
  export type { EnvSecretStoreOptions } from "./envSecretStore.js";
13
+ export { createOpenAIDimensionEstimator } from "./openAIDimensionEstimator.js";
14
+ export type { OpenAIDimensionEstimatorOptions } from "./openAIDimensionEstimator.js";
package/dist/index.js CHANGED
@@ -7,3 +7,4 @@
7
7
  export { createCorreiosHttpClient } from "./correiosHttpClient.js";
8
8
  export { InMemorySettingsStore } from "./inMemorySettingsStore.js";
9
9
  export { EnvSecretStore } from "./envSecretStore.js";
10
+ export { createOpenAIDimensionEstimator } from "./openAIDimensionEstimator.js";
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `ProductDimensionEstimator` implemented with the OpenAI chat-completions API.
3
+ *
4
+ * Ports the dimension-estimation path from the Velo `openAI/` modules:
5
+ * - `ai-tools.js` -> the `estimate_dimensions` tool + request/response handling
6
+ * - `prompt-methods.js`-> `createDimensionEstimationPrompt`
7
+ * - `box-methods.js` -> `validateAndOptimizeDimensions` (post-process AI output)
8
+ *
9
+ * `fetch` is injectable (defaults to global fetch) so this also runs under Velo
10
+ * with `wix-fetch` passed in. The API key is passed in — the caller resolves it
11
+ * (e.g. via a `SecretStore`), keeping secret handling out of this module.
12
+ */
13
+ import type { ProductDimensionEstimator } from "@meuecommerce/frete";
14
+ type FetchLike = typeof globalThis.fetch;
15
+ export interface OpenAIDimensionEstimatorOptions {
16
+ apiKey: string;
17
+ fetch?: FetchLike;
18
+ model?: string;
19
+ temperature?: number;
20
+ apiUrl?: string;
21
+ timeoutMs?: number;
22
+ }
23
+ export declare function createOpenAIDimensionEstimator(options: OpenAIDimensionEstimatorOptions): ProductDimensionEstimator;
24
+ export {};
@@ -0,0 +1,127 @@
1
+ const ESTIMATE_DIMENSIONS_TOOL = {
2
+ type: "function",
3
+ function: {
4
+ name: "estimate_dimensions",
5
+ description: "Estima dimensões de produtos PRESERVANDO os nomes exatos fornecidos",
6
+ parameters: {
7
+ type: "object",
8
+ properties: {
9
+ products: {
10
+ type: "array",
11
+ items: {
12
+ type: "object",
13
+ properties: {
14
+ name: { type: "string" },
15
+ length: { type: "number", description: "Comprimento em cm" },
16
+ width: { type: "number", description: "Largura em cm" },
17
+ height: { type: "number", description: "Altura em cm" },
18
+ },
19
+ required: ["name", "length", "width", "height"],
20
+ },
21
+ },
22
+ },
23
+ required: ["products"],
24
+ },
25
+ },
26
+ };
27
+ function largestBox(boxes) {
28
+ return boxes.reduce((largest, current) => current.width * current.height * current.length > largest.width * largest.height * largest.length ? current : largest);
29
+ }
30
+ function createPrompt(products, boxes, samples) {
31
+ const box = largestBox(boxes);
32
+ const maxWidth = Math.floor(box.width * 0.8);
33
+ const maxHeight = Math.floor(box.height * 0.8);
34
+ const maxLength = Math.floor(box.length * 0.8);
35
+ const samplesSection = samples.length > 0
36
+ ? `\n🌟 EXEMPLOS DO USUÁRIO (PRIORIDADE MÁXIMA):\n${samples.map((s, i) => `${i + 1}. ${s}`).join("\n")}\n\n` +
37
+ `📋 INSTRUÇÕES:\n1. Para cada produto, procure um exemplo similar acima\n2. Match exato → use dimensões EXATAS\n3. Similar → ajuste\n4. Sem match → conhecimento geral\n`
38
+ : `\n📋 INSTRUÇÕES (SEM EXEMPLOS):\n1. Considere a embalagem comercial típica brasileira\n2. Use conhecimento sobre produtos similares\n3. Ajuste baseado no peso\n`;
39
+ return (`Você é um especialista em dimensões de produtos brasileiros para e-commerce.\n\n` +
40
+ `🚨 LIMITES FÍSICOS OBRIGATÓRIOS:\n- Largura máxima: ${maxWidth}cm\n- Altura máxima: ${maxHeight}cm\n- Comprimento máximo: ${maxLength}cm\n(Maior caixa: ${box.name ?? "—"})\n\n` +
41
+ `📦 PRODUTOS PARA ESTIMAR:\n${products.map((p, i) => `${i + 1}. ${p.name} - ${p.quantity}x (${p.weight}kg cada)`).join("\n")}\n` +
42
+ samplesSection +
43
+ `\n⚠️ REGRAS: respeite os limites; preserve os nomes EXATOS; retorne length×width×height em cm.\n` +
44
+ `Responda usando a função 'estimate_dimensions'.`);
45
+ }
46
+ /** Apply the largest-box physical limits + a minimum-volume floor to AI output. */
47
+ function validateAndOptimize(aiResult, products, boxes) {
48
+ const box = largestBox(boxes);
49
+ const maxWidth = Math.floor(box.width * 0.8);
50
+ const maxHeight = Math.floor(box.height * 0.8);
51
+ const maxLength = Math.floor(box.length * 0.8);
52
+ const optimized = aiResult.products.map((product) => {
53
+ let { length, width, height } = product;
54
+ if (length > maxLength)
55
+ length = maxLength;
56
+ if (width > maxWidth)
57
+ width = maxWidth;
58
+ if (height > maxHeight)
59
+ height = maxHeight;
60
+ const volume = length * width * height;
61
+ const original = products.find((p) => p.name === product.name);
62
+ const minVolume = (original?.weight || 0.1) * 500;
63
+ if (volume > 0 && volume < minVolume) {
64
+ const scale = Math.cbrt(minVolume / volume);
65
+ length = Math.min(maxLength, length * scale);
66
+ width = Math.min(maxWidth, width * scale);
67
+ height = Math.min(maxHeight, height * scale);
68
+ }
69
+ return {
70
+ name: product.name,
71
+ length: Math.round(length * 10) / 10,
72
+ width: Math.round(width * 10) / 10,
73
+ height: Math.round(height * 10) / 10,
74
+ };
75
+ });
76
+ return { products: optimized };
77
+ }
78
+ export function createOpenAIDimensionEstimator(options) {
79
+ const { apiKey, fetch = globalThis.fetch, model = "gpt-4o-mini", temperature = 0.1, apiUrl = "https://api.openai.com/v1/chat/completions", timeoutMs = 20000, } = options;
80
+ if (!apiKey)
81
+ throw new Error("createOpenAIDimensionEstimator: apiKey is required");
82
+ if (typeof fetch !== "function")
83
+ throw new Error("createOpenAIDimensionEstimator: no fetch available");
84
+ return {
85
+ async estimate(products, boxes, productSamples = []) {
86
+ if (!boxes.length)
87
+ throw new Error("estimate: no boxes provided");
88
+ const body = {
89
+ model,
90
+ messages: [
91
+ {
92
+ role: "system",
93
+ content: "Você é um especialista em dimensões de produtos. Use os exemplos personalizados como prioridade máxima quando o nome bater.",
94
+ },
95
+ { role: "user", content: createPrompt(products, boxes, productSamples) },
96
+ ],
97
+ tools: [ESTIMATE_DIMENSIONS_TOOL],
98
+ tool_choice: { type: "function", function: { name: "estimate_dimensions" } },
99
+ temperature,
100
+ };
101
+ const controller = new AbortController();
102
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
103
+ let response;
104
+ try {
105
+ response = await fetch(apiUrl, {
106
+ method: "POST",
107
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
108
+ body: JSON.stringify(body),
109
+ signal: controller.signal,
110
+ });
111
+ }
112
+ finally {
113
+ clearTimeout(timer);
114
+ }
115
+ if (!response.ok)
116
+ throw new Error(`OpenAI API Error: ${response.status} ${response.statusText}`);
117
+ const json = (await response.json());
118
+ if (json.error)
119
+ throw new Error(`OpenAI Error: ${json.error.message}`);
120
+ const args = json.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments;
121
+ if (!args)
122
+ throw new Error("IA não conseguiu estimar dimensões dos produtos");
123
+ const parsed = JSON.parse(args);
124
+ return validateAndOptimize(parsed, products, boxes);
125
+ },
126
+ };
127
+ }
package/package.json CHANGED
@@ -1,22 +1,27 @@
1
1
  {
2
2
  "name": "@meuecommerce/frete-adapter-node",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Node implementation of @meuecommerce/frete ports (Correios HTTP client) using global fetch. Used by the Shopify/Fly deployment and the MCP server.",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
6
+ "main": "./dist/cjs/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/cjs/index.js",
13
+ "default": "./dist/cjs/index.js"
12
14
  }
13
15
  },
14
- "files": ["dist"],
16
+ "files": [
17
+ "dist"
18
+ ],
15
19
  "engines": {
16
20
  "node": ">=18.18"
17
21
  },
18
22
  "scripts": {
19
- "build": "tsc -p tsconfig.json",
23
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node ../../scripts/cjs-pkg.cjs",
24
+ "prepublishOnly": "npm run build",
20
25
  "typecheck": "tsc --noEmit",
21
26
  "test": "vitest run",
22
27
  "test:watch": "vitest"
@@ -26,6 +31,7 @@
26
31
  "access": "public"
27
32
  },
28
33
  "dependencies": {
29
- "@meuecommerce/frete": "^0.1.0"
30
- }
34
+ "@meuecommerce/frete": "^0.2.0"
35
+ },
36
+ "module": "./dist/index.js"
31
37
  }