teraprox-core-sdk 0.3.1 → 0.3.3

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,112 @@ 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";
58
- function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }) {
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, emulator) {
69
+ var _a, _b;
70
+ if (emulator) {
71
+ return {
72
+ type: "emulator",
73
+ host: emulator.host,
74
+ port: emulator.port,
75
+ namespace: (_a = emulator.namespace) != null ? _a : "teraprox-default-rtdb"
76
+ };
77
+ }
78
+ const emulatorHostEnv = typeof process !== "undefined" && process.env.REACT_APP_RTDB_EMULATOR_HOST || void 0;
79
+ if (emulatorHostEnv) {
80
+ const [host, portStr] = emulatorHostEnv.split(":");
81
+ const port = parseInt(portStr != null ? portStr : "9000", 10);
82
+ const namespace = (_b = process.env.REACT_APP_RTDB_EMULATOR_NS) != null ? _b : "demo-local";
83
+ return { type: "emulator", host, port, namespace };
84
+ }
85
+ if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
86
+ return { type: "cloud", config: firebaseConfig };
87
+ }
88
+ const alive = await probeEmulator("localhost", 9e3);
89
+ if (alive) {
90
+ return { type: "emulator", host: "localhost", port: 9e3, namespace: "demo-local" };
91
+ }
92
+ return { type: "none" };
93
+ }
94
+ var _emulatorConnected = /* @__PURE__ */ new Set();
95
+ function RtdbConfigWarning({ onDismiss }) {
96
+ return /* @__PURE__ */ jsxs(
97
+ "div",
98
+ {
99
+ style: {
100
+ position: "fixed",
101
+ bottom: 24,
102
+ right: 24,
103
+ zIndex: 99999,
104
+ background: "#1a1a2e",
105
+ color: "#fff",
106
+ borderRadius: 8,
107
+ padding: "16px 20px",
108
+ maxWidth: 420,
109
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
110
+ borderLeft: "4px solid #f59e0b",
111
+ fontFamily: "monospace",
112
+ fontSize: 13,
113
+ lineHeight: 1.6
114
+ },
115
+ children: [
116
+ /* @__PURE__ */ jsx2("div", { style: { fontWeight: "bold", marginBottom: 8, color: "#f59e0b", fontSize: 14 }, children: "\u26A0 RTDB n\xE3o configurado" }),
117
+ /* @__PURE__ */ jsxs("div", { children: [
118
+ "Matching Objects em tempo real desativados.",
119
+ /* @__PURE__ */ jsx2("br", {}),
120
+ "Configure no ",
121
+ /* @__PURE__ */ jsx2("code", { children: ".env.dev" }),
122
+ ":",
123
+ /* @__PURE__ */ jsx2("br", {}),
124
+ /* @__PURE__ */ jsx2("code", { style: { color: "#86efac" }, children: "REACT_APP_RTDB_EMULATOR_HOST=localhost:9000" }),
125
+ /* @__PURE__ */ jsx2("br", {}),
126
+ /* @__PURE__ */ jsxs("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
127
+ "ou inicie o backend com ",
128
+ /* @__PURE__ */ jsx2("code", { children: "RtdbEmulatorPlugin" }),
129
+ " do @onroad/core"
130
+ ] }),
131
+ /* @__PURE__ */ jsx2("br", {}),
132
+ /* @__PURE__ */ jsxs("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
133
+ "e rode o emulator com ",
134
+ /* @__PURE__ */ jsx2("code", { children: "firebase emulators:start --only database" })
135
+ ] })
136
+ ] }),
137
+ /* @__PURE__ */ jsx2(
138
+ "button",
139
+ {
140
+ onClick: onDismiss,
141
+ style: {
142
+ marginTop: 12,
143
+ padding: "4px 14px",
144
+ background: "#374151",
145
+ color: "#fff",
146
+ border: "none",
147
+ borderRadius: 4,
148
+ cursor: "pointer",
149
+ fontSize: 12
150
+ },
151
+ children: "Fechar"
152
+ }
153
+ )
154
+ ]
155
+ }
156
+ );
157
+ }
158
+ function StandaloneProvider({ createController, addToast, firebaseConfig, emulator, tenant, children }) {
59
159
  const [subscriptions] = useState([]);
60
160
  const subscriptionsRef = useRef(subscriptions);
61
161
  subscriptionsRef.current = subscriptions;
162
+ const [rtdbWarning, setRtdbWarning] = useState(false);
62
163
  const toast = useMemo(
63
164
  () => ({
64
165
  success: (msg, opts) => addToast(msg, { appearance: "success", autoDismiss: true, ...opts }),
@@ -84,16 +185,49 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
84
185
  [subscriptions]
85
186
  );
86
187
  useEffect2(() => {
87
- if (!firebaseConfig || !tenant || Object.keys(firebaseConfig).length === 0) return;
188
+ if (!tenant) return;
88
189
  let cleanup;
190
+ let cancelled = false;
89
191
  (async () => {
192
+ var _a;
193
+ const strategy = await resolveRtdbStrategy(firebaseConfig, emulator);
194
+ if (cancelled) return;
195
+ if (strategy.type === "none") {
196
+ if (process.env.NODE_ENV !== "production") setRtdbWarning(true);
197
+ return;
198
+ }
90
199
  try {
91
200
  const { initializeApp, getApps } = await import("firebase/app");
92
- const { getDatabase, ref, onChildAdded, off } = await import("firebase/database");
93
- if (!getApps().length) {
94
- initializeApp(firebaseConfig);
201
+ const { getDatabase, ref, onChildAdded } = await import("firebase/database");
202
+ let db;
203
+ if (strategy.type === "emulator") {
204
+ const appName = `onroad-rtdb-emulator-${strategy.port}`;
205
+ const existingApp = getApps().find((a) => a.name === appName);
206
+ const emulatorApp = existingApp != null ? existingApp : initializeApp(
207
+ {
208
+ projectId: strategy.namespace,
209
+ databaseURL: `http://${strategy.host}:${strategy.port}?ns=${strategy.namespace}`
210
+ },
211
+ appName
212
+ );
213
+ db = getDatabase(emulatorApp);
214
+ if (!_emulatorConnected.has(appName)) {
215
+ const { connectDatabaseEmulator } = await import("firebase/database");
216
+ try {
217
+ connectDatabaseEmulator(db, strategy.host, strategy.port);
218
+ _emulatorConnected.add(appName);
219
+ } catch (e) {
220
+ _emulatorConnected.add(appName);
221
+ }
222
+ }
223
+ console.info(
224
+ `[StandaloneProvider] RTDB emulator at ${strategy.host}:${strategy.port} (ns=${strategy.namespace})`
225
+ );
226
+ } else {
227
+ const existingApp = (_a = getApps().find((a) => a.name === "[DEFAULT]")) != null ? _a : getApps()[0];
228
+ const cloudApp = existingApp != null ? existingApp : initializeApp(strategy.config);
229
+ db = getDatabase(cloudApp);
95
230
  }
96
- const db = getDatabase();
97
231
  const moRef = ref(db, `${tenant}/matchingObjects`);
98
232
  const unsub = onChildAdded(moRef, (snapshot) => {
99
233
  const data = snapshot.val();
@@ -115,17 +249,16 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
115
249
  }
116
250
  }
117
251
  });
118
- cleanup = () => {
119
- off(moRef, "child_added", unsub);
120
- };
252
+ cleanup = () => unsub();
121
253
  } catch (err) {
122
- console.warn("[StandaloneProvider] Firebase RTDB listener failed \u2014 continuing without real-time:", err);
254
+ console.warn("[StandaloneProvider] Firebase RTDB listener failed:", err);
123
255
  }
124
256
  })();
125
257
  return () => {
258
+ cancelled = true;
126
259
  cleanup == null ? void 0 : cleanup();
127
260
  };
128
- }, [firebaseConfig, tenant]);
261
+ }, [firebaseConfig, emulator, tenant]);
129
262
  const value = useMemo(
130
263
  () => ({
131
264
  createController,
@@ -142,7 +275,10 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
142
275
  }),
143
276
  [createController, toast, subscribe, unsubscribe]
144
277
  );
145
- return /* @__PURE__ */ jsx2(CoreServiceContext.Provider, { value, children });
278
+ return /* @__PURE__ */ jsxs(CoreServiceContext.Provider, { value, children: [
279
+ children,
280
+ rtdbWarning && /* @__PURE__ */ jsx2(RtdbConfigWarning, { onDismiss: () => setRtdbWarning(false) })
281
+ ] });
146
282
  }
147
283
 
148
284
  // src/federation/DevAutoLogin.tsx
@@ -119,6 +119,11 @@ interface ReducersBundle {
119
119
  }
120
120
  declare function createReducersBundle(config: ReducersBundleConfig): ReducersBundle;
121
121
 
122
+ interface EmulatorConfig {
123
+ host: string;
124
+ port: number;
125
+ namespace?: string;
126
+ }
122
127
  interface StandaloneConfig {
123
128
  /**
124
129
  * Factory for creating an HttpController. The remote provides its
@@ -131,8 +136,22 @@ interface StandaloneConfig {
131
136
  * Firebase client config object (from REACT_APP_FIREBASE_CONFIG).
132
137
  * When provided together with `tenant`, enables real-time matching
133
138
  * object listening via Firebase RTDB — same path the Core uses.
139
+ *
140
+ * If omitted, the provider auto-detects the RTDB emulator:
141
+ * 1. Uses `emulator` prop if provided
142
+ * 2. Reads `REACT_APP_RTDB_EMULATOR_HOST` env var
143
+ * 3. Falls back to probing `localhost:9000`
144
+ * 4. If neither works, shows a configuration popup (dev only)
134
145
  */
135
146
  firebaseConfig?: Record<string, unknown>;
147
+ /**
148
+ * Explicit emulator configuration. Preferred over env vars because
149
+ * DefinePlugin may not replace process.env.REACT_APP_* inside node_modules.
150
+ * The app should read its own env vars and pass them here.
151
+ *
152
+ * Example: `{ host: 'localhost', port: 9000, namespace: 'teraprox-default-rtdb' }`
153
+ */
154
+ emulator?: EmulatorConfig;
136
155
  /**
137
156
  * Tenant / company ID used to build the RTDB path:
138
157
  * `{tenant}/matchingObjects`
@@ -146,9 +165,11 @@ interface StandaloneConfig {
146
165
  * Replaces the StandaloneCoreServiceProvider that each remote copies.
147
166
  * The remote only needs to provide its createController and addToast.
148
167
  *
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.
168
+ * RTDB auto-resolution order (no config required):
169
+ * 1. `REACT_APP_RTDB_EMULATOR_HOST` env var connects to emulator
170
+ * 2. `firebaseConfig` prop connects to Firebase cloud
171
+ * 3. Auto-probe `localhost:9000` → uses emulator if alive
172
+ * 4. None found → shows configuration popup (dev only)
152
173
  *
153
174
  * USAGE IN REMOTES:
154
175
  * ```
@@ -158,13 +179,11 @@ interface StandaloneConfig {
158
179
  *
159
180
  * function App() {
160
181
  * const { addToast } = useToasts()
161
- * const firebaseConfig = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG || '{}')
162
182
  * const tenant = useSelector(s => s.global.companyId)
163
183
  * return (
164
184
  * <StandaloneProvider
165
185
  * createController={(ctx, ep) => basicController(ctx, ep)}
166
186
  * addToast={addToast}
167
- * firebaseConfig={firebaseConfig}
168
187
  * tenant={tenant}
169
188
  * >
170
189
  * <Routes />
@@ -173,7 +192,7 @@ interface StandaloneConfig {
173
192
  * }
174
193
  * ```
175
194
  */
176
- declare function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }: StandaloneConfig): react_jsx_runtime.JSX.Element;
195
+ declare function StandaloneProvider({ createController, addToast, firebaseConfig, emulator, tenant, children }: StandaloneConfig): react_jsx_runtime.JSX.Element;
177
196
 
178
197
  interface DevUser {
179
198
  firstName: string;
@@ -119,6 +119,11 @@ interface ReducersBundle {
119
119
  }
120
120
  declare function createReducersBundle(config: ReducersBundleConfig): ReducersBundle;
121
121
 
122
+ interface EmulatorConfig {
123
+ host: string;
124
+ port: number;
125
+ namespace?: string;
126
+ }
122
127
  interface StandaloneConfig {
123
128
  /**
124
129
  * Factory for creating an HttpController. The remote provides its
@@ -131,8 +136,22 @@ interface StandaloneConfig {
131
136
  * Firebase client config object (from REACT_APP_FIREBASE_CONFIG).
132
137
  * When provided together with `tenant`, enables real-time matching
133
138
  * object listening via Firebase RTDB — same path the Core uses.
139
+ *
140
+ * If omitted, the provider auto-detects the RTDB emulator:
141
+ * 1. Uses `emulator` prop if provided
142
+ * 2. Reads `REACT_APP_RTDB_EMULATOR_HOST` env var
143
+ * 3. Falls back to probing `localhost:9000`
144
+ * 4. If neither works, shows a configuration popup (dev only)
134
145
  */
135
146
  firebaseConfig?: Record<string, unknown>;
147
+ /**
148
+ * Explicit emulator configuration. Preferred over env vars because
149
+ * DefinePlugin may not replace process.env.REACT_APP_* inside node_modules.
150
+ * The app should read its own env vars and pass them here.
151
+ *
152
+ * Example: `{ host: 'localhost', port: 9000, namespace: 'teraprox-default-rtdb' }`
153
+ */
154
+ emulator?: EmulatorConfig;
136
155
  /**
137
156
  * Tenant / company ID used to build the RTDB path:
138
157
  * `{tenant}/matchingObjects`
@@ -146,9 +165,11 @@ interface StandaloneConfig {
146
165
  * Replaces the StandaloneCoreServiceProvider that each remote copies.
147
166
  * The remote only needs to provide its createController and addToast.
148
167
  *
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.
168
+ * RTDB auto-resolution order (no config required):
169
+ * 1. `REACT_APP_RTDB_EMULATOR_HOST` env var connects to emulator
170
+ * 2. `firebaseConfig` prop connects to Firebase cloud
171
+ * 3. Auto-probe `localhost:9000` → uses emulator if alive
172
+ * 4. None found → shows configuration popup (dev only)
152
173
  *
153
174
  * USAGE IN REMOTES:
154
175
  * ```
@@ -158,13 +179,11 @@ interface StandaloneConfig {
158
179
  *
159
180
  * function App() {
160
181
  * const { addToast } = useToasts()
161
- * const firebaseConfig = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG || '{}')
162
182
  * const tenant = useSelector(s => s.global.companyId)
163
183
  * return (
164
184
  * <StandaloneProvider
165
185
  * createController={(ctx, ep) => basicController(ctx, ep)}
166
186
  * addToast={addToast}
167
- * firebaseConfig={firebaseConfig}
168
187
  * tenant={tenant}
169
188
  * >
170
189
  * <Routes />
@@ -173,7 +192,7 @@ interface StandaloneConfig {
173
192
  * }
174
193
  * ```
175
194
  */
176
- declare function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }: StandaloneConfig): react_jsx_runtime.JSX.Element;
195
+ declare function StandaloneProvider({ createController, addToast, firebaseConfig, emulator, tenant, children }: StandaloneConfig): react_jsx_runtime.JSX.Element;
177
196
 
178
197
  interface DevUser {
179
198
  firstName: string;
@@ -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-UNcGnNB-.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-UNcGnNB-.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -96,10 +96,111 @@ 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
- function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }) {
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, emulator) {
110
+ var _a, _b;
111
+ if (emulator) {
112
+ return {
113
+ type: "emulator",
114
+ host: emulator.host,
115
+ port: emulator.port,
116
+ namespace: (_a = emulator.namespace) != null ? _a : "teraprox-default-rtdb"
117
+ };
118
+ }
119
+ const emulatorHostEnv = typeof process !== "undefined" && process.env.REACT_APP_RTDB_EMULATOR_HOST || void 0;
120
+ if (emulatorHostEnv) {
121
+ const [host, portStr] = emulatorHostEnv.split(":");
122
+ const port = parseInt(portStr != null ? portStr : "9000", 10);
123
+ const namespace = (_b = process.env.REACT_APP_RTDB_EMULATOR_NS) != null ? _b : "demo-local";
124
+ return { type: "emulator", host, port, namespace };
125
+ }
126
+ if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
127
+ return { type: "cloud", config: firebaseConfig };
128
+ }
129
+ const alive = await probeEmulator("localhost", 9e3);
130
+ if (alive) {
131
+ return { type: "emulator", host: "localhost", port: 9e3, namespace: "demo-local" };
132
+ }
133
+ return { type: "none" };
134
+ }
135
+ var _emulatorConnected = /* @__PURE__ */ new Set();
136
+ function RtdbConfigWarning({ onDismiss }) {
137
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
138
+ "div",
139
+ {
140
+ style: {
141
+ position: "fixed",
142
+ bottom: 24,
143
+ right: 24,
144
+ zIndex: 99999,
145
+ background: "#1a1a2e",
146
+ color: "#fff",
147
+ borderRadius: 8,
148
+ padding: "16px 20px",
149
+ maxWidth: 420,
150
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
151
+ borderLeft: "4px solid #f59e0b",
152
+ fontFamily: "monospace",
153
+ fontSize: 13,
154
+ lineHeight: 1.6
155
+ },
156
+ children: [
157
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { fontWeight: "bold", marginBottom: 8, color: "#f59e0b", fontSize: 14 }, children: "\u26A0 RTDB n\xE3o configurado" }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
159
+ "Matching Objects em tempo real desativados.",
160
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
161
+ "Configure no ",
162
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: ".env.dev" }),
163
+ ":",
164
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
165
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { style: { color: "#86efac" }, children: "REACT_APP_RTDB_EMULATOR_HOST=localhost:9000" }),
166
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
167
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
168
+ "ou inicie o backend com ",
169
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: "RtdbEmulatorPlugin" }),
170
+ " do @onroad/core"
171
+ ] }),
172
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
173
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
174
+ "e rode o emulator com ",
175
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: "firebase emulators:start --only database" })
176
+ ] })
177
+ ] }),
178
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
179
+ "button",
180
+ {
181
+ onClick: onDismiss,
182
+ style: {
183
+ marginTop: 12,
184
+ padding: "4px 14px",
185
+ background: "#374151",
186
+ color: "#fff",
187
+ border: "none",
188
+ borderRadius: 4,
189
+ cursor: "pointer",
190
+ fontSize: 12
191
+ },
192
+ children: "Fechar"
193
+ }
194
+ )
195
+ ]
196
+ }
197
+ );
198
+ }
199
+ function StandaloneProvider({ createController, addToast, firebaseConfig, emulator, tenant, children }) {
100
200
  const [subscriptions] = (0, import_react3.useState)([]);
101
201
  const subscriptionsRef = (0, import_react3.useRef)(subscriptions);
102
202
  subscriptionsRef.current = subscriptions;
203
+ const [rtdbWarning, setRtdbWarning] = (0, import_react3.useState)(false);
103
204
  const toast = (0, import_react3.useMemo)(
104
205
  () => ({
105
206
  success: (msg, opts) => addToast(msg, { appearance: "success", autoDismiss: true, ...opts }),
@@ -125,16 +226,49 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
125
226
  [subscriptions]
126
227
  );
127
228
  (0, import_react3.useEffect)(() => {
128
- if (!firebaseConfig || !tenant || Object.keys(firebaseConfig).length === 0) return;
229
+ if (!tenant) return;
129
230
  let cleanup;
231
+ let cancelled = false;
130
232
  (async () => {
233
+ var _a;
234
+ const strategy = await resolveRtdbStrategy(firebaseConfig, emulator);
235
+ if (cancelled) return;
236
+ if (strategy.type === "none") {
237
+ if (process.env.NODE_ENV !== "production") setRtdbWarning(true);
238
+ return;
239
+ }
131
240
  try {
132
241
  const { initializeApp, getApps } = await import("firebase/app");
133
- const { getDatabase, ref, onChildAdded, off } = await import("firebase/database");
134
- if (!getApps().length) {
135
- initializeApp(firebaseConfig);
242
+ const { getDatabase, ref, onChildAdded } = await import("firebase/database");
243
+ let db;
244
+ if (strategy.type === "emulator") {
245
+ const appName = `onroad-rtdb-emulator-${strategy.port}`;
246
+ const existingApp = getApps().find((a) => a.name === appName);
247
+ const emulatorApp = existingApp != null ? existingApp : initializeApp(
248
+ {
249
+ projectId: strategy.namespace,
250
+ databaseURL: `http://${strategy.host}:${strategy.port}?ns=${strategy.namespace}`
251
+ },
252
+ appName
253
+ );
254
+ db = getDatabase(emulatorApp);
255
+ if (!_emulatorConnected.has(appName)) {
256
+ const { connectDatabaseEmulator } = await import("firebase/database");
257
+ try {
258
+ connectDatabaseEmulator(db, strategy.host, strategy.port);
259
+ _emulatorConnected.add(appName);
260
+ } catch (e) {
261
+ _emulatorConnected.add(appName);
262
+ }
263
+ }
264
+ console.info(
265
+ `[StandaloneProvider] RTDB emulator at ${strategy.host}:${strategy.port} (ns=${strategy.namespace})`
266
+ );
267
+ } else {
268
+ const existingApp = (_a = getApps().find((a) => a.name === "[DEFAULT]")) != null ? _a : getApps()[0];
269
+ const cloudApp = existingApp != null ? existingApp : initializeApp(strategy.config);
270
+ db = getDatabase(cloudApp);
136
271
  }
137
- const db = getDatabase();
138
272
  const moRef = ref(db, `${tenant}/matchingObjects`);
139
273
  const unsub = onChildAdded(moRef, (snapshot) => {
140
274
  const data = snapshot.val();
@@ -156,17 +290,16 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
156
290
  }
157
291
  }
158
292
  });
159
- cleanup = () => {
160
- off(moRef, "child_added", unsub);
161
- };
293
+ cleanup = () => unsub();
162
294
  } catch (err) {
163
- console.warn("[StandaloneProvider] Firebase RTDB listener failed \u2014 continuing without real-time:", err);
295
+ console.warn("[StandaloneProvider] Firebase RTDB listener failed:", err);
164
296
  }
165
297
  })();
166
298
  return () => {
299
+ cancelled = true;
167
300
  cleanup == null ? void 0 : cleanup();
168
301
  };
169
- }, [firebaseConfig, tenant]);
302
+ }, [firebaseConfig, emulator, tenant]);
170
303
  const value = (0, import_react3.useMemo)(
171
304
  () => ({
172
305
  createController,
@@ -183,7 +316,10 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
183
316
  }),
184
317
  [createController, toast, subscribe, unsubscribe]
185
318
  );
186
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CoreServiceContext.Provider, { value, children });
319
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(CoreServiceContext.Provider, { value, children: [
320
+ children,
321
+ rtdbWarning && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RtdbConfigWarning, { onDismiss: () => setRtdbWarning(false) })
322
+ ] });
187
323
  }
188
324
 
189
325
  // 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-CLNYFYWU.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-UNcGnNB-.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-UNcGnNB-.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-UNcGnNB-.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-UNcGnNB-.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,111 @@ 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
- function StandaloneProvider({ createController, addToast, firebaseConfig, tenant, children }) {
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, emulator) {
1357
+ var _a, _b;
1358
+ if (emulator) {
1359
+ return {
1360
+ type: "emulator",
1361
+ host: emulator.host,
1362
+ port: emulator.port,
1363
+ namespace: (_a = emulator.namespace) != null ? _a : "teraprox-default-rtdb"
1364
+ };
1365
+ }
1366
+ const emulatorHostEnv = typeof process !== "undefined" && process.env.REACT_APP_RTDB_EMULATOR_HOST || void 0;
1367
+ if (emulatorHostEnv) {
1368
+ const [host, portStr] = emulatorHostEnv.split(":");
1369
+ const port = parseInt(portStr != null ? portStr : "9000", 10);
1370
+ const namespace = (_b = process.env.REACT_APP_RTDB_EMULATOR_NS) != null ? _b : "demo-local";
1371
+ return { type: "emulator", host, port, namespace };
1372
+ }
1373
+ if (firebaseConfig && Object.keys(firebaseConfig).length > 0) {
1374
+ return { type: "cloud", config: firebaseConfig };
1375
+ }
1376
+ const alive = await probeEmulator("localhost", 9e3);
1377
+ if (alive) {
1378
+ return { type: "emulator", host: "localhost", port: 9e3, namespace: "demo-local" };
1379
+ }
1380
+ return { type: "none" };
1381
+ }
1382
+ var _emulatorConnected = /* @__PURE__ */ new Set();
1383
+ function RtdbConfigWarning({ onDismiss }) {
1384
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1385
+ "div",
1386
+ {
1387
+ style: {
1388
+ position: "fixed",
1389
+ bottom: 24,
1390
+ right: 24,
1391
+ zIndex: 99999,
1392
+ background: "#1a1a2e",
1393
+ color: "#fff",
1394
+ borderRadius: 8,
1395
+ padding: "16px 20px",
1396
+ maxWidth: 420,
1397
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
1398
+ borderLeft: "4px solid #f59e0b",
1399
+ fontFamily: "monospace",
1400
+ fontSize: 13,
1401
+ lineHeight: 1.6
1402
+ },
1403
+ children: [
1404
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontWeight: "bold", marginBottom: 8, color: "#f59e0b", fontSize: 14 }, children: "\u26A0 RTDB n\xE3o configurado" }),
1405
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1406
+ "Matching Objects em tempo real desativados.",
1407
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1408
+ "Configure no ",
1409
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: ".env.dev" }),
1410
+ ":",
1411
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { style: { color: "#86efac" }, children: "REACT_APP_RTDB_EMULATOR_HOST=localhost:9000" }),
1413
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
1415
+ "ou inicie o backend com ",
1416
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: "RtdbEmulatorPlugin" }),
1417
+ " do @onroad/core"
1418
+ ] }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("br", {}),
1420
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { color: "#94a3b8", fontSize: 11 }, children: [
1421
+ "e rode o emulator com ",
1422
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { children: "firebase emulators:start --only database" })
1423
+ ] })
1424
+ ] }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1426
+ "button",
1427
+ {
1428
+ onClick: onDismiss,
1429
+ style: {
1430
+ marginTop: 12,
1431
+ padding: "4px 14px",
1432
+ background: "#374151",
1433
+ color: "#fff",
1434
+ border: "none",
1435
+ borderRadius: 4,
1436
+ cursor: "pointer",
1437
+ fontSize: 12
1438
+ },
1439
+ children: "Fechar"
1440
+ }
1441
+ )
1442
+ ]
1443
+ }
1444
+ );
1445
+ }
1446
+ function StandaloneProvider({ createController, addToast, firebaseConfig, emulator, tenant, children }) {
1347
1447
  const [subscriptions] = (0, import_react19.useState)([]);
1348
1448
  const subscriptionsRef = (0, import_react19.useRef)(subscriptions);
1349
1449
  subscriptionsRef.current = subscriptions;
1450
+ const [rtdbWarning, setRtdbWarning] = (0, import_react19.useState)(false);
1350
1451
  const toast = (0, import_react19.useMemo)(
1351
1452
  () => ({
1352
1453
  success: (msg, opts) => addToast(msg, { appearance: "success", autoDismiss: true, ...opts }),
@@ -1372,16 +1473,49 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
1372
1473
  [subscriptions]
1373
1474
  );
1374
1475
  (0, import_react19.useEffect)(() => {
1375
- if (!firebaseConfig || !tenant || Object.keys(firebaseConfig).length === 0) return;
1476
+ if (!tenant) return;
1376
1477
  let cleanup;
1478
+ let cancelled = false;
1377
1479
  (async () => {
1480
+ var _a;
1481
+ const strategy = await resolveRtdbStrategy(firebaseConfig, emulator);
1482
+ if (cancelled) return;
1483
+ if (strategy.type === "none") {
1484
+ if (process.env.NODE_ENV !== "production") setRtdbWarning(true);
1485
+ return;
1486
+ }
1378
1487
  try {
1379
1488
  const { initializeApp, getApps } = await import("firebase/app");
1380
- const { getDatabase, ref, onChildAdded, off } = await import("firebase/database");
1381
- if (!getApps().length) {
1382
- initializeApp(firebaseConfig);
1489
+ const { getDatabase, ref, onChildAdded } = await import("firebase/database");
1490
+ let db;
1491
+ if (strategy.type === "emulator") {
1492
+ const appName = `onroad-rtdb-emulator-${strategy.port}`;
1493
+ const existingApp = getApps().find((a) => a.name === appName);
1494
+ const emulatorApp = existingApp != null ? existingApp : initializeApp(
1495
+ {
1496
+ projectId: strategy.namespace,
1497
+ databaseURL: `http://${strategy.host}:${strategy.port}?ns=${strategy.namespace}`
1498
+ },
1499
+ appName
1500
+ );
1501
+ db = getDatabase(emulatorApp);
1502
+ if (!_emulatorConnected.has(appName)) {
1503
+ const { connectDatabaseEmulator } = await import("firebase/database");
1504
+ try {
1505
+ connectDatabaseEmulator(db, strategy.host, strategy.port);
1506
+ _emulatorConnected.add(appName);
1507
+ } catch (e) {
1508
+ _emulatorConnected.add(appName);
1509
+ }
1510
+ }
1511
+ console.info(
1512
+ `[StandaloneProvider] RTDB emulator at ${strategy.host}:${strategy.port} (ns=${strategy.namespace})`
1513
+ );
1514
+ } else {
1515
+ const existingApp = (_a = getApps().find((a) => a.name === "[DEFAULT]")) != null ? _a : getApps()[0];
1516
+ const cloudApp = existingApp != null ? existingApp : initializeApp(strategy.config);
1517
+ db = getDatabase(cloudApp);
1383
1518
  }
1384
- const db = getDatabase();
1385
1519
  const moRef = ref(db, `${tenant}/matchingObjects`);
1386
1520
  const unsub = onChildAdded(moRef, (snapshot) => {
1387
1521
  const data = snapshot.val();
@@ -1403,17 +1537,16 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
1403
1537
  }
1404
1538
  }
1405
1539
  });
1406
- cleanup = () => {
1407
- off(moRef, "child_added", unsub);
1408
- };
1540
+ cleanup = () => unsub();
1409
1541
  } catch (err) {
1410
- console.warn("[StandaloneProvider] Firebase RTDB listener failed \u2014 continuing without real-time:", err);
1542
+ console.warn("[StandaloneProvider] Firebase RTDB listener failed:", err);
1411
1543
  }
1412
1544
  })();
1413
1545
  return () => {
1546
+ cancelled = true;
1414
1547
  cleanup == null ? void 0 : cleanup();
1415
1548
  };
1416
- }, [firebaseConfig, tenant]);
1549
+ }, [firebaseConfig, emulator, tenant]);
1417
1550
  const value = (0, import_react19.useMemo)(
1418
1551
  () => ({
1419
1552
  createController,
@@ -1430,7 +1563,10 @@ function StandaloneProvider({ createController, addToast, firebaseConfig, tenant
1430
1563
  }),
1431
1564
  [createController, toast, subscribe, unsubscribe]
1432
1565
  );
1433
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CoreServiceContext.Provider, { value, children });
1566
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CoreServiceContext.Provider, { value, children: [
1567
+ children,
1568
+ rtdbWarning && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RtdbConfigWarning, { onDismiss: () => setRtdbWarning(false) })
1569
+ ] });
1434
1570
  }
1435
1571
 
1436
1572
  // 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-CLNYFYWU.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.3",
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",
@@ -46,7 +46,9 @@
46
46
  "react-router-dom": ">=6.0.0"
47
47
  },
48
48
  "peerDependenciesMeta": {
49
- "firebase": { "optional": true }
49
+ "firebase": {
50
+ "optional": true
51
+ }
50
52
  },
51
53
  "devDependencies": {
52
54
  "@reduxjs/toolkit": "^2.0.0",