teraprox-core-sdk 0.3.1 → 0.3.2

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.
@@ -54,11 +54,104 @@ function createReducersBundle(config) {
54
54
 
55
55
  // src/federation/StandaloneProvider.tsx
56
56
  import { useMemo, useCallback, useState, useEffect as useEffect2, useRef } from "react";
57
- import { jsx as jsx2 } from "react/jsx-runtime";
57
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
58
+ async function probeEmulator(host, port) {
59
+ try {
60
+ const res = await fetch(`http://${host}:${port}/.json`, {
61
+ signal: AbortSignal.timeout(1500)
62
+ });
63
+ return res.ok || res.status === 404;
64
+ } catch (e) {
65
+ return false;
66
+ }
67
+ }
68
+ async function resolveRtdbStrategy(firebaseConfig) {
69
+ var _a;
70
+ const emulatorHostEnv = typeof process !== "undefined" && process.env.REACT_APP_RTDB_EMULATOR_HOST || void 0;
71
+ if (emulatorHostEnv) {
72
+ const [host, portStr] = emulatorHostEnv.split(":");
73
+ const port = parseInt(portStr != null ? portStr : "9000", 10);
74
+ const namespace = (_a = process.env.REACT_APP_RTDB_EMULATOR_NS) != null ? _a : "demo-local";
75
+ return { type: "emulator", host, port, namespace };
76
+ }
77
+ if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
78
+ return { type: "cloud", config: firebaseConfig };
79
+ }
80
+ const alive = await probeEmulator("localhost", 9e3);
81
+ if (alive) {
82
+ return { type: "emulator", host: "localhost", port: 9e3, namespace: "demo-local" };
83
+ }
84
+ return { type: "none" };
85
+ }
86
+ var _emulatorConnected = /* @__PURE__ */ new Set();
87
+ function RtdbConfigWarning({ onDismiss }) {
88
+ return /* @__PURE__ */ jsxs(
89
+ "div",
90
+ {
91
+ style: {
92
+ position: "fixed",
93
+ bottom: 24,
94
+ right: 24,
95
+ zIndex: 99999,
96
+ background: "#1a1a2e",
97
+ color: "#fff",
98
+ borderRadius: 8,
99
+ padding: "16px 20px",
100
+ maxWidth: 420,
101
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
102
+ borderLeft: "4px solid #f59e0b",
103
+ fontFamily: "monospace",
104
+ fontSize: 13,
105
+ lineHeight: 1.6
106
+ },
107
+ children: [
108
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: "bold", marginBottom: 8, color: "#f59e0b", fontSize: 14 }, children: "\u26A0 RTDB n\xE3o configurado" }),
109
+ /* @__PURE__ */ jsxs("div", { children: [
110
+ "Matching Objects em tempo real desativados.",
111
+ /* @__PURE__ */ jsx2("br", {}),
112
+ "Configure no ",
113
+ /* @__PURE__ */ jsx2("code", { children: ".env.dev" }),
114
+ ":",
115
+ /* @__PURE__ */ jsx2("br", {}),
116
+ /* @__PURE__ */ jsx2("code", { style: { color: "#86efac" }, children: "REACT_APP_RTDB_EMULATOR_HOST=localhost:9000" }),
117
+ /* @__PURE__ */ jsx2("br", {}),
118
+ /* @__PURE__ */ jsxs("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
119
+ "ou inicie o backend com ",
120
+ /* @__PURE__ */ jsx2("code", { children: "RtdbEmulatorPlugin" }),
121
+ " do @onroad/core"
122
+ ] }),
123
+ /* @__PURE__ */ jsx2("br", {}),
124
+ /* @__PURE__ */ jsxs("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
125
+ "e rode o emulator com ",
126
+ /* @__PURE__ */ jsx2("code", { children: "firebase emulators:start --only database" })
127
+ ] })
128
+ ] }),
129
+ /* @__PURE__ */ jsx2(
130
+ "button",
131
+ {
132
+ onClick: onDismiss,
133
+ style: {
134
+ marginTop: 12,
135
+ padding: "4px 14px",
136
+ background: "#374151",
137
+ color: "#fff",
138
+ border: "none",
139
+ borderRadius: 4,
140
+ cursor: "pointer",
141
+ fontSize: 12
142
+ },
143
+ children: "Fechar"
144
+ }
145
+ )
146
+ ]
147
+ }
148
+ );
149
+ }
58
150
  function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }) {
59
151
  const [subscriptions] = useState([]);
60
152
  const subscriptionsRef = useRef(subscriptions);
61
153
  subscriptionsRef.current = subscriptions;
154
+ const [rtdbWarning, setRtdbWarning] = useState(false);
62
155
  const toast = useMemo(
63
156
  () => ({
64
157
  success: (msg, opts) => addToast(msg, { appearance: "success", autoDismiss: true, ...opts }),
@@ -84,16 +177,49 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
84
177
  [subscriptions]
85
178
  );
86
179
  useEffect2(() => {
87
- if (!firebaseConfig || !tenant || Object.keys(firebaseConfig).length === 0) return;
180
+ if (!tenant) return;
88
181
  let cleanup;
182
+ let cancelled = false;
89
183
  (async () => {
184
+ var _a;
185
+ const strategy = await resolveRtdbStrategy(firebaseConfig);
186
+ if (cancelled) return;
187
+ if (strategy.type === "none") {
188
+ if (process.env.NODE_ENV !== "production") setRtdbWarning(true);
189
+ return;
190
+ }
90
191
  try {
91
192
  const { initializeApp, getApps } = await import("firebase/app");
92
- const { getDatabase, ref, onChildAdded, off } = await import("firebase/database");
93
- if (!getApps().length) {
94
- initializeApp(firebaseConfig);
193
+ const { getDatabase, ref, onChildAdded } = await import("firebase/database");
194
+ let db;
195
+ if (strategy.type === "emulator") {
196
+ const appName = `onroad-rtdb-emulator-${strategy.port}`;
197
+ const existingApp = getApps().find((a) => a.name === appName);
198
+ const emulatorApp = existingApp != null ? existingApp : initializeApp(
199
+ {
200
+ projectId: strategy.namespace,
201
+ databaseURL: `http://${strategy.host}:${strategy.port}?ns=${strategy.namespace}`
202
+ },
203
+ appName
204
+ );
205
+ db = getDatabase(emulatorApp);
206
+ if (!_emulatorConnected.has(appName)) {
207
+ const { connectDatabaseEmulator } = await import("firebase/database");
208
+ try {
209
+ connectDatabaseEmulator(db, strategy.host, strategy.port);
210
+ _emulatorConnected.add(appName);
211
+ } catch (e) {
212
+ _emulatorConnected.add(appName);
213
+ }
214
+ }
215
+ console.info(
216
+ `[StandaloneProvider] RTDB emulator at ${strategy.host}:${strategy.port} (ns=${strategy.namespace})`
217
+ );
218
+ } else {
219
+ const existingApp = (_a = getApps().find((a) => a.name === "[DEFAULT]")) != null ? _a : getApps()[0];
220
+ const cloudApp = existingApp != null ? existingApp : initializeApp(strategy.config);
221
+ db = getDatabase(cloudApp);
95
222
  }
96
- const db = getDatabase();
97
223
  const moRef = ref(db, `${tenant}/matchingObjects`);
98
224
  const unsub = onChildAdded(moRef, (snapshot) => {
99
225
  const data = snapshot.val();
@@ -115,14 +241,13 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
115
241
  }
116
242
  }
117
243
  });
118
- cleanup = () => {
119
- off(moRef, "child_added", unsub);
120
- };
244
+ cleanup = () => unsub();
121
245
  } catch (err) {
122
- console.warn("[StandaloneProvider] Firebase RTDB listener failed \u2014 continuing without real-time:", err);
246
+ console.warn("[StandaloneProvider] Firebase RTDB listener failed:", err);
123
247
  }
124
248
  })();
125
249
  return () => {
250
+ cancelled = true;
126
251
  cleanup == null ? void 0 : cleanup();
127
252
  };
128
253
  }, [firebaseConfig, tenant]);
@@ -142,7 +267,10 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
142
267
  }),
143
268
  [createController, toast, subscribe, unsubscribe]
144
269
  );
145
- return /* @__PURE__ */ jsx2(CoreServiceContext.Provider, { value, children });
270
+ return /* @__PURE__ */ jsxs(CoreServiceContext.Provider, { value, children: [
271
+ children,
272
+ rtdbWarning && /* @__PURE__ */ jsx2(RtdbConfigWarning, { onDismiss: () => setRtdbWarning(false) })
273
+ ] });
146
274
  }
147
275
 
148
276
  // src/federation/DevAutoLogin.tsx
@@ -131,6 +131,11 @@ interface StandaloneConfig {
131
131
  * Firebase client config object (from REACT_APP_FIREBASE_CONFIG).
132
132
  * When provided together with `tenant`, enables real-time matching
133
133
  * object listening via Firebase RTDB — same path the Core uses.
134
+ *
135
+ * If omitted, the provider auto-detects the RTDB emulator:
136
+ * 1. Reads `REACT_APP_RTDB_EMULATOR_HOST` (e.g. `localhost:9000`)
137
+ * 2. Falls back to probing `localhost:9000`
138
+ * 3. If neither works, shows a configuration popup (dev only)
134
139
  */
135
140
  firebaseConfig?: Record<string, unknown>;
136
141
  /**
@@ -146,9 +151,11 @@ interface StandaloneConfig {
146
151
  * Replaces the StandaloneCoreServiceProvider that each remote copies.
147
152
  * The remote only needs to provide its createController and addToast.
148
153
  *
149
- * When `firebaseConfig` + `tenant` are provided the provider connects
150
- * to Firebase RTDB and dispatches incoming matching objects to all
151
- * active subscribers mirroring the behaviour of the Core host.
154
+ * RTDB auto-resolution order (no config required):
155
+ * 1. `REACT_APP_RTDB_EMULATOR_HOST` env var connects to emulator
156
+ * 2. `firebaseConfig` prop connects to Firebase cloud
157
+ * 3. Auto-probe `localhost:9000` → uses emulator if alive
158
+ * 4. None found → shows configuration popup (dev only)
152
159
  *
153
160
  * USAGE IN REMOTES:
154
161
  * ```
@@ -158,13 +165,11 @@ interface StandaloneConfig {
158
165
  *
159
166
  * function App() {
160
167
  * const { addToast } = useToasts()
161
- * const firebaseConfig = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG || '{}')
162
168
  * const tenant = useSelector(s => s.global.companyId)
163
169
  * return (
164
170
  * <StandaloneProvider
165
171
  * createController={(ctx, ep) => basicController(ctx, ep)}
166
172
  * addToast={addToast}
167
- * firebaseConfig={firebaseConfig}
168
173
  * tenant={tenant}
169
174
  * >
170
175
  * <Routes />
@@ -131,6 +131,11 @@ interface StandaloneConfig {
131
131
  * Firebase client config object (from REACT_APP_FIREBASE_CONFIG).
132
132
  * When provided together with `tenant`, enables real-time matching
133
133
  * object listening via Firebase RTDB — same path the Core uses.
134
+ *
135
+ * If omitted, the provider auto-detects the RTDB emulator:
136
+ * 1. Reads `REACT_APP_RTDB_EMULATOR_HOST` (e.g. `localhost:9000`)
137
+ * 2. Falls back to probing `localhost:9000`
138
+ * 3. If neither works, shows a configuration popup (dev only)
134
139
  */
135
140
  firebaseConfig?: Record<string, unknown>;
136
141
  /**
@@ -146,9 +151,11 @@ interface StandaloneConfig {
146
151
  * Replaces the StandaloneCoreServiceProvider that each remote copies.
147
152
  * The remote only needs to provide its createController and addToast.
148
153
  *
149
- * When `firebaseConfig` + `tenant` are provided the provider connects
150
- * to Firebase RTDB and dispatches incoming matching objects to all
151
- * active subscribers mirroring the behaviour of the Core host.
154
+ * RTDB auto-resolution order (no config required):
155
+ * 1. `REACT_APP_RTDB_EMULATOR_HOST` env var connects to emulator
156
+ * 2. `firebaseConfig` prop connects to Firebase cloud
157
+ * 3. Auto-probe `localhost:9000` → uses emulator if alive
158
+ * 4. None found → shows configuration popup (dev only)
152
159
  *
153
160
  * USAGE IN REMOTES:
154
161
  * ```
@@ -158,13 +165,11 @@ interface StandaloneConfig {
158
165
  *
159
166
  * function App() {
160
167
  * const { addToast } = useToasts()
161
- * const firebaseConfig = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG || '{}')
162
168
  * const tenant = useSelector(s => s.global.companyId)
163
169
  * return (
164
170
  * <StandaloneProvider
165
171
  * createController={(ctx, ep) => basicController(ctx, ep)}
166
172
  * addToast={addToast}
167
- * firebaseConfig={firebaseConfig}
168
173
  * tenant={tenant}
169
174
  * >
170
175
  * <Routes />
@@ -1,3 +1,3 @@
1
- export { D as DevAutoLogin, F as FederatedBridge, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, f as createReducersBundle } from './federation-BANKXvQ5.mjs';
1
+ export { D as DevAutoLogin, F as FederatedBridge, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, f as createReducersBundle } from './federation-B4ShfmDd.mjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { D as DevAutoLogin, F as FederatedBridge, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, f as createReducersBundle } from './federation-BANKXvQ5.js';
1
+ export { D as DevAutoLogin, F as FederatedBridge, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, f as createReducersBundle } from './federation-B4ShfmDd.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -96,10 +96,103 @@ function createReducersBundle(config) {
96
96
  // src/federation/StandaloneProvider.tsx
97
97
  var import_react3 = require("react");
98
98
  var import_jsx_runtime2 = require("react/jsx-runtime");
99
+ async function probeEmulator(host, port) {
100
+ try {
101
+ const res = await fetch(`http://${host}:${port}/.json`, {
102
+ signal: AbortSignal.timeout(1500)
103
+ });
104
+ return res.ok || res.status === 404;
105
+ } catch (e) {
106
+ return false;
107
+ }
108
+ }
109
+ async function resolveRtdbStrategy(firebaseConfig) {
110
+ var _a;
111
+ const emulatorHostEnv = typeof process !== "undefined" && process.env.REACT_APP_RTDB_EMULATOR_HOST || void 0;
112
+ if (emulatorHostEnv) {
113
+ const [host, portStr] = emulatorHostEnv.split(":");
114
+ const port = parseInt(portStr != null ? portStr : "9000", 10);
115
+ const namespace = (_a = process.env.REACT_APP_RTDB_EMULATOR_NS) != null ? _a : "demo-local";
116
+ return { type: "emulator", host, port, namespace };
117
+ }
118
+ if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
119
+ return { type: "cloud", config: firebaseConfig };
120
+ }
121
+ const alive = await probeEmulator("localhost", 9e3);
122
+ if (alive) {
123
+ return { type: "emulator", host: "localhost", port: 9e3, namespace: "demo-local" };
124
+ }
125
+ return { type: "none" };
126
+ }
127
+ var _emulatorConnected = /* @__PURE__ */ new Set();
128
+ function RtdbConfigWarning({ onDismiss }) {
129
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
130
+ "div",
131
+ {
132
+ style: {
133
+ position: "fixed",
134
+ bottom: 24,
135
+ right: 24,
136
+ zIndex: 99999,
137
+ background: "#1a1a2e",
138
+ color: "#fff",
139
+ borderRadius: 8,
140
+ padding: "16px 20px",
141
+ maxWidth: 420,
142
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
143
+ borderLeft: "4px solid #f59e0b",
144
+ fontFamily: "monospace",
145
+ fontSize: 13,
146
+ lineHeight: 1.6
147
+ },
148
+ children: [
149
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontWeight: "bold", marginBottom: 8, color: "#f59e0b", fontSize: 14 }, children: "\u26A0 RTDB n\xE3o configurado" }),
150
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
151
+ "Matching Objects em tempo real desativados.",
152
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
153
+ "Configure no ",
154
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: ".env.dev" }),
155
+ ":",
156
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
157
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: { color: "#86efac" }, children: "REACT_APP_RTDB_EMULATOR_HOST=localhost:9000" }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
159
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
160
+ "ou inicie o backend com ",
161
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: "RtdbEmulatorPlugin" }),
162
+ " do @onroad/core"
163
+ ] }),
164
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
165
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
166
+ "e rode o emulator com ",
167
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: "firebase emulators:start --only database" })
168
+ ] })
169
+ ] }),
170
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
171
+ "button",
172
+ {
173
+ onClick: onDismiss,
174
+ style: {
175
+ marginTop: 12,
176
+ padding: "4px 14px",
177
+ background: "#374151",
178
+ color: "#fff",
179
+ border: "none",
180
+ borderRadius: 4,
181
+ cursor: "pointer",
182
+ fontSize: 12
183
+ },
184
+ children: "Fechar"
185
+ }
186
+ )
187
+ ]
188
+ }
189
+ );
190
+ }
99
191
  function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }) {
100
192
  const [subscriptions] = (0, import_react3.useState)([]);
101
193
  const subscriptionsRef = (0, import_react3.useRef)(subscriptions);
102
194
  subscriptionsRef.current = subscriptions;
195
+ const [rtdbWarning, setRtdbWarning] = (0, import_react3.useState)(false);
103
196
  const toast = (0, import_react3.useMemo)(
104
197
  () => ({
105
198
  success: (msg, opts) => addToast(msg, { appearance: "success", autoDismiss: true, ...opts }),
@@ -125,16 +218,49 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
125
218
  [subscriptions]
126
219
  );
127
220
  (0, import_react3.useEffect)(() => {
128
- if (!firebaseConfig || !tenant || Object.keys(firebaseConfig).length === 0) return;
221
+ if (!tenant) return;
129
222
  let cleanup;
223
+ let cancelled = false;
130
224
  (async () => {
225
+ var _a;
226
+ const strategy = await resolveRtdbStrategy(firebaseConfig);
227
+ if (cancelled) return;
228
+ if (strategy.type === "none") {
229
+ if (process.env.NODE_ENV !== "production") setRtdbWarning(true);
230
+ return;
231
+ }
131
232
  try {
132
233
  const { initializeApp, getApps } = await import("firebase/app");
133
- const { getDatabase, ref, onChildAdded, off } = await import("firebase/database");
134
- if (!getApps().length) {
135
- initializeApp(firebaseConfig);
234
+ const { getDatabase, ref, onChildAdded } = await import("firebase/database");
235
+ let db;
236
+ if (strategy.type === "emulator") {
237
+ const appName = `onroad-rtdb-emulator-${strategy.port}`;
238
+ const existingApp = getApps().find((a) => a.name === appName);
239
+ const emulatorApp = existingApp != null ? existingApp : initializeApp(
240
+ {
241
+ projectId: strategy.namespace,
242
+ databaseURL: `http://${strategy.host}:${strategy.port}?ns=${strategy.namespace}`
243
+ },
244
+ appName
245
+ );
246
+ db = getDatabase(emulatorApp);
247
+ if (!_emulatorConnected.has(appName)) {
248
+ const { connectDatabaseEmulator } = await import("firebase/database");
249
+ try {
250
+ connectDatabaseEmulator(db, strategy.host, strategy.port);
251
+ _emulatorConnected.add(appName);
252
+ } catch (e) {
253
+ _emulatorConnected.add(appName);
254
+ }
255
+ }
256
+ console.info(
257
+ `[StandaloneProvider] RTDB emulator at ${strategy.host}:${strategy.port} (ns=${strategy.namespace})`
258
+ );
259
+ } else {
260
+ const existingApp = (_a = getApps().find((a) => a.name === "[DEFAULT]")) != null ? _a : getApps()[0];
261
+ const cloudApp = existingApp != null ? existingApp : initializeApp(strategy.config);
262
+ db = getDatabase(cloudApp);
136
263
  }
137
- const db = getDatabase();
138
264
  const moRef = ref(db, `${tenant}/matchingObjects`);
139
265
  const unsub = onChildAdded(moRef, (snapshot) => {
140
266
  const data = snapshot.val();
@@ -156,14 +282,13 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
156
282
  }
157
283
  }
158
284
  });
159
- cleanup = () => {
160
- off(moRef, "child_added", unsub);
161
- };
285
+ cleanup = () => unsub();
162
286
  } catch (err) {
163
- console.warn("[StandaloneProvider] Firebase RTDB listener failed \u2014 continuing without real-time:", err);
287
+ console.warn("[StandaloneProvider] Firebase RTDB listener failed:", err);
164
288
  }
165
289
  })();
166
290
  return () => {
291
+ cancelled = true;
167
292
  cleanup == null ? void 0 : cleanup();
168
293
  };
169
294
  }, [firebaseConfig, tenant]);
@@ -183,7 +308,10 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
183
308
  }),
184
309
  [createController, toast, subscribe, unsubscribe]
185
310
  );
186
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CoreServiceContext.Provider, { value, children });
311
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(CoreServiceContext.Provider, { value, children: [
312
+ children,
313
+ rtdbWarning && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RtdbConfigWarning, { onDismiss: () => setRtdbWarning(false) })
314
+ ] });
187
315
  }
188
316
 
189
317
  // src/federation/DevAutoLogin.tsx
@@ -3,7 +3,7 @@ import {
3
3
  FederatedBridge,
4
4
  StandaloneProvider,
5
5
  createReducersBundle
6
- } from "./chunk-N7KKTEAH.mjs";
6
+ } from "./chunk-PATJPPYP.mjs";
7
7
  export {
8
8
  DevAutoLogin,
9
9
  FederatedBridge,
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CoreService, H as HttpController, T as ToastService } from './federation-BANKXvQ5.mjs';
2
- export { D as DevAutoLogin, F as FederatedBridge, M as MatchingObjectSubscription, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, e as ToastOptions, f as createReducersBundle } from './federation-BANKXvQ5.mjs';
1
+ import { C as CoreService, H as HttpController, T as ToastService } from './federation-B4ShfmDd.mjs';
2
+ export { D as DevAutoLogin, F as FederatedBridge, M as MatchingObjectSubscription, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, e as ToastOptions, f as createReducersBundle } from './federation-B4ShfmDd.mjs';
3
3
  import * as React from 'react';
4
4
  import { Dispatch } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CoreService, H as HttpController, T as ToastService } from './federation-BANKXvQ5.js';
2
- export { D as DevAutoLogin, F as FederatedBridge, M as MatchingObjectSubscription, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, e as ToastOptions, f as createReducersBundle } from './federation-BANKXvQ5.js';
1
+ import { C as CoreService, H as HttpController, T as ToastService } from './federation-B4ShfmDd.js';
2
+ export { D as DevAutoLogin, F as FederatedBridge, M as MatchingObjectSubscription, R as ReducersBundle, a as ReducersBundleConfig, b as RemoteManifest, c as RemoteMenuItem, d as RemoteMenuSection, S as StandaloneProvider, e as ToastOptions, f as createReducersBundle } from './federation-B4ShfmDd.js';
3
3
  import * as React from 'react';
4
4
  import { Dispatch } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
package/dist/index.js CHANGED
@@ -1343,10 +1343,103 @@ function createReducersBundle(config) {
1343
1343
  // src/federation/StandaloneProvider.tsx
1344
1344
  var import_react19 = require("react");
1345
1345
  var import_jsx_runtime8 = require("react/jsx-runtime");
1346
+ async function probeEmulator(host, port) {
1347
+ try {
1348
+ const res = await fetch(`http://${host}:${port}/.json`, {
1349
+ signal: AbortSignal.timeout(1500)
1350
+ });
1351
+ return res.ok || res.status === 404;
1352
+ } catch (e) {
1353
+ return false;
1354
+ }
1355
+ }
1356
+ async function resolveRtdbStrategy(firebaseConfig) {
1357
+ var _a;
1358
+ const emulatorHostEnv = typeof process !== "undefined" && process.env.REACT_APP_RTDB_EMULATOR_HOST || void 0;
1359
+ if (emulatorHostEnv) {
1360
+ const [host, portStr] = emulatorHostEnv.split(":");
1361
+ const port = parseInt(portStr != null ? portStr : "9000", 10);
1362
+ const namespace = (_a = process.env.REACT_APP_RTDB_EMULATOR_NS) != null ? _a : "demo-local";
1363
+ return { type: "emulator", host, port, namespace };
1364
+ }
1365
+ if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
1366
+ return { type: "cloud", config: firebaseConfig };
1367
+ }
1368
+ const alive = await probeEmulator("localhost", 9e3);
1369
+ if (alive) {
1370
+ return { type: "emulator", host: "localhost", port: 9e3, namespace: "demo-local" };
1371
+ }
1372
+ return { type: "none" };
1373
+ }
1374
+ var _emulatorConnected = /* @__PURE__ */ new Set();
1375
+ function RtdbConfigWarning({ onDismiss }) {
1376
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1377
+ "div",
1378
+ {
1379
+ style: {
1380
+ position: "fixed",
1381
+ bottom: 24,
1382
+ right: 24,
1383
+ zIndex: 99999,
1384
+ background: "#1a1a2e",
1385
+ color: "#fff",
1386
+ borderRadius: 8,
1387
+ padding: "16px 20px",
1388
+ maxWidth: 420,
1389
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
1390
+ borderLeft: "4px solid #f59e0b",
1391
+ fontFamily: "monospace",
1392
+ fontSize: 13,
1393
+ lineHeight: 1.6
1394
+ },
1395
+ children: [
1396
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontWeight: "bold", marginBottom: 8, color: "#f59e0b", fontSize: 14 }, children: "\u26A0 RTDB n\xE3o configurado" }),
1397
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1398
+ "Matching Objects em tempo real desativados.",
1399
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1400
+ "Configure no ",
1401
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: ".env.dev" }),
1402
+ ":",
1403
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1404
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { style: { color: "#86efac" }, children: "REACT_APP_RTDB_EMULATOR_HOST=localhost:9000" }),
1405
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1406
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
1407
+ "ou inicie o backend com ",
1408
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: "RtdbEmulatorPlugin" }),
1409
+ " do @onroad/core"
1410
+ ] }),
1411
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
1413
+ "e rode o emulator com ",
1414
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: "firebase emulators:start --only database" })
1415
+ ] })
1416
+ ] }),
1417
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1418
+ "button",
1419
+ {
1420
+ onClick: onDismiss,
1421
+ style: {
1422
+ marginTop: 12,
1423
+ padding: "4px 14px",
1424
+ background: "#374151",
1425
+ color: "#fff",
1426
+ border: "none",
1427
+ borderRadius: 4,
1428
+ cursor: "pointer",
1429
+ fontSize: 12
1430
+ },
1431
+ children: "Fechar"
1432
+ }
1433
+ )
1434
+ ]
1435
+ }
1436
+ );
1437
+ }
1346
1438
  function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }) {
1347
1439
  const [subscriptions] = (0, import_react19.useState)([]);
1348
1440
  const subscriptionsRef = (0, import_react19.useRef)(subscriptions);
1349
1441
  subscriptionsRef.current = subscriptions;
1442
+ const [rtdbWarning, setRtdbWarning] = (0, import_react19.useState)(false);
1350
1443
  const toast = (0, import_react19.useMemo)(
1351
1444
  () => ({
1352
1445
  success: (msg, opts) => addToast(msg, { appearance: "success", autoDismiss: true, ...opts }),
@@ -1372,16 +1465,49 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
1372
1465
  [subscriptions]
1373
1466
  );
1374
1467
  (0, import_react19.useEffect)(() => {
1375
- if (!firebaseConfig || !tenant || Object.keys(firebaseConfig).length === 0) return;
1468
+ if (!tenant) return;
1376
1469
  let cleanup;
1470
+ let cancelled = false;
1377
1471
  (async () => {
1472
+ var _a;
1473
+ const strategy = await resolveRtdbStrategy(firebaseConfig);
1474
+ if (cancelled) return;
1475
+ if (strategy.type === "none") {
1476
+ if (process.env.NODE_ENV !== "production") setRtdbWarning(true);
1477
+ return;
1478
+ }
1378
1479
  try {
1379
1480
  const { initializeApp, getApps } = await import("firebase/app");
1380
- const { getDatabase, ref, onChildAdded, off } = await import("firebase/database");
1381
- if (!getApps().length) {
1382
- initializeApp(firebaseConfig);
1481
+ const { getDatabase, ref, onChildAdded } = await import("firebase/database");
1482
+ let db;
1483
+ if (strategy.type === "emulator") {
1484
+ const appName = `onroad-rtdb-emulator-${strategy.port}`;
1485
+ const existingApp = getApps().find((a) => a.name === appName);
1486
+ const emulatorApp = existingApp != null ? existingApp : initializeApp(
1487
+ {
1488
+ projectId: strategy.namespace,
1489
+ databaseURL: `http://${strategy.host}:${strategy.port}?ns=${strategy.namespace}`
1490
+ },
1491
+ appName
1492
+ );
1493
+ db = getDatabase(emulatorApp);
1494
+ if (!_emulatorConnected.has(appName)) {
1495
+ const { connectDatabaseEmulator } = await import("firebase/database");
1496
+ try {
1497
+ connectDatabaseEmulator(db, strategy.host, strategy.port);
1498
+ _emulatorConnected.add(appName);
1499
+ } catch (e) {
1500
+ _emulatorConnected.add(appName);
1501
+ }
1502
+ }
1503
+ console.info(
1504
+ `[StandaloneProvider] RTDB emulator at ${strategy.host}:${strategy.port} (ns=${strategy.namespace})`
1505
+ );
1506
+ } else {
1507
+ const existingApp = (_a = getApps().find((a) => a.name === "[DEFAULT]")) != null ? _a : getApps()[0];
1508
+ const cloudApp = existingApp != null ? existingApp : initializeApp(strategy.config);
1509
+ db = getDatabase(cloudApp);
1383
1510
  }
1384
- const db = getDatabase();
1385
1511
  const moRef = ref(db, `${tenant}/matchingObjects`);
1386
1512
  const unsub = onChildAdded(moRef, (snapshot) => {
1387
1513
  const data = snapshot.val();
@@ -1403,14 +1529,13 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
1403
1529
  }
1404
1530
  }
1405
1531
  });
1406
- cleanup = () => {
1407
- off(moRef, "child_added", unsub);
1408
- };
1532
+ cleanup = () => unsub();
1409
1533
  } catch (err) {
1410
- console.warn("[StandaloneProvider] Firebase RTDB listener failed \u2014 continuing without real-time:", err);
1534
+ console.warn("[StandaloneProvider] Firebase RTDB listener failed:", err);
1411
1535
  }
1412
1536
  })();
1413
1537
  return () => {
1538
+ cancelled = true;
1414
1539
  cleanup == null ? void 0 : cleanup();
1415
1540
  };
1416
1541
  }, [firebaseConfig, tenant]);
@@ -1430,7 +1555,10 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
1430
1555
  }),
1431
1556
  [createController, toast, subscribe, unsubscribe]
1432
1557
  );
1433
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CoreServiceContext.Provider, { value, children });
1558
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CoreServiceContext.Provider, { value, children: [
1559
+ children,
1560
+ rtdbWarning && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RtdbConfigWarning, { onDismiss: () => setRtdbWarning(false) })
1561
+ ] });
1434
1562
  }
1435
1563
 
1436
1564
  // src/federation/DevAutoLogin.tsx
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  FederatedBridge,
5
5
  StandaloneProvider,
6
6
  createReducersBundle
7
- } from "./chunk-N7KKTEAH.mjs";
7
+ } from "./chunk-PATJPPYP.mjs";
8
8
 
9
9
  // src/hooks/useCoreService.ts
10
10
  import { useContext } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teraprox-core-sdk",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Contrato tipado Core ↔ Federados — interfaces, context, hooks e componentes compartilhados",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",