@eventusgo/sdk 0.1.2 → 0.1.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.
@@ -0,0 +1,394 @@
1
+ // src/index.ts
2
+ import QRCode from "qrcode";
3
+ var EventusBridgeUnavailableError = class extends Error {
4
+ constructor(message = "No Eventus bridge is connected.") {
5
+ super(message);
6
+ this.name = "EventusBridgeUnavailableError";
7
+ }
8
+ };
9
+ function isBridgeUnavailableError(error) {
10
+ return error instanceof Error && error.name === "EventusBridgeUnavailableError";
11
+ }
12
+ var EventEmitter = class {
13
+ listeners = /* @__PURE__ */ new Map();
14
+ on(event, handler) {
15
+ const nextListeners = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
16
+ nextListeners.add(handler);
17
+ this.listeners.set(event, nextListeners);
18
+ return () => {
19
+ nextListeners.delete(handler);
20
+ if (nextListeners.size === 0) {
21
+ this.listeners.delete(event);
22
+ }
23
+ };
24
+ }
25
+ emit(event, payload) {
26
+ const handlers = this.listeners.get(event);
27
+ if (!handlers) {
28
+ return;
29
+ }
30
+ for (const handler of handlers) {
31
+ handler(payload);
32
+ }
33
+ }
34
+ };
35
+ function getBrowserLocale() {
36
+ if (typeof navigator !== "undefined" && navigator.language) {
37
+ return navigator.language;
38
+ }
39
+ return "en";
40
+ }
41
+ function createDefaultMockContext() {
42
+ const manifest = readInjectedManifest();
43
+ return {
44
+ locale: getBrowserLocale(),
45
+ theme: "light",
46
+ ...manifest?.appId ? { appId: manifest.appId } : {},
47
+ user: {
48
+ id: "dev-user",
49
+ name: "Eventus Developer"
50
+ }
51
+ };
52
+ }
53
+ function readInjectedManifest() {
54
+ const manifest = globalThis.__EVENTUS_MANIFEST__;
55
+ if (typeof manifest === "undefined") {
56
+ return null;
57
+ }
58
+ return manifest ?? null;
59
+ }
60
+ function escapeHtml(value) {
61
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
62
+ }
63
+ function isAbsoluteHttpUrl(value) {
64
+ if (typeof value !== "string" || !value.trim()) {
65
+ return false;
66
+ }
67
+ try {
68
+ const parsed = new URL(value);
69
+ return (parsed.protocol === "http:" || parsed.protocol === "https:") && !!parsed.hostname;
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+ function parseDevPreviewDescriptor(value) {
75
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
76
+ return null;
77
+ }
78
+ const descriptor = value;
79
+ const manifest = descriptor.manifest;
80
+ const urls = descriptor.urls;
81
+ if (descriptor.type !== "eventus-dev-preview" || descriptor.version !== 1 || !manifest || typeof manifest !== "object" || Array.isArray(manifest) || !urls || typeof urls !== "object" || Array.isArray(urls)) {
82
+ return null;
83
+ }
84
+ const nextManifest = manifest;
85
+ const nextUrls = urls;
86
+ const permissions = nextManifest.permissions;
87
+ if (typeof nextManifest.name !== "string" || !nextManifest.name.trim() || typeof nextManifest.appId !== "string" || !nextManifest.appId.trim() || typeof nextManifest.version !== "string" || !nextManifest.version.trim() || !isAbsoluteHttpUrl(nextUrls.local) || (nextUrls.network !== null && nextUrls.network !== void 0 ? !isAbsoluteHttpUrl(nextUrls.network) : false) || !isAbsoluteHttpUrl(nextUrls.preview) || permissions !== void 0 && (!Array.isArray(permissions) || permissions.some((permission) => typeof permission !== "string"))) {
88
+ return null;
89
+ }
90
+ return {
91
+ type: "eventus-dev-preview",
92
+ version: 1,
93
+ manifest: {
94
+ name: nextManifest.name,
95
+ appId: nextManifest.appId,
96
+ version: nextManifest.version,
97
+ ...permissions ? { permissions } : {}
98
+ },
99
+ urls: {
100
+ local: nextUrls.local,
101
+ network: nextUrls.network === void 0 ? null : nextUrls.network,
102
+ preview: nextUrls.preview
103
+ }
104
+ };
105
+ }
106
+ async function fetchDevPreviewDescriptor() {
107
+ if (typeof fetch !== "function") {
108
+ return null;
109
+ }
110
+ try {
111
+ const response = await fetch("/__eventus__/preview.json", {
112
+ headers: {
113
+ Accept: "application/json"
114
+ }
115
+ });
116
+ if (!response.ok) {
117
+ return null;
118
+ }
119
+ return parseDevPreviewDescriptor(await response.json());
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+ function renderPreviewValue(label, value) {
125
+ const escaped = escapeHtml(value);
126
+ return `
127
+ <div>
128
+ <dt>${escapeHtml(label)}</dt>
129
+ <dd><code>${escaped}</code></dd>
130
+ </div>
131
+ `;
132
+ }
133
+ function renderPermissions(permissions) {
134
+ const value = permissions && permissions.length > 0 ? permissions.join(", ") : "None";
135
+ return renderPreviewValue("Permissions", value);
136
+ }
137
+ function createLivePreviewHtml(preview, qrSvg) {
138
+ return `
139
+ <section aria-label="Eventus live preview handoff" style="margin:24px 0;display:grid;gap:20px;">
140
+ <style>
141
+ .eventus-live-preview{display:grid;gap:20px;}
142
+ .eventus-live-preview__copy{color:#102347;}
143
+ .eventus-live-preview__copy h2{margin:0 0 12px;font-size:1.4rem;line-height:1.2;}
144
+ .eventus-live-preview__copy p{margin:0;color:#465d82;line-height:1.6;}
145
+ .eventus-live-preview__grid{display:grid;gap:14px;margin:18px 0 0;}
146
+ .eventus-live-preview__grid > div{padding:14px 16px;border-radius:16px;background:#f8fbff;border:1px solid rgba(16,35,71,0.08);}
147
+ .eventus-live-preview__grid dt{font-size:0.72rem;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#6880a6;}
148
+ .eventus-live-preview__grid dd{margin:6px 0 0;color:#102347;word-break:break-word;}
149
+ .eventus-live-preview__grid code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:0.9rem;}
150
+ .eventus-live-preview__hint{margin-top:16px !important;color:#465d82 !important;}
151
+ .eventus-live-preview__qr{display:grid;place-items:center;padding:18px;border-radius:24px;background:linear-gradient(180deg,#fefefe 0%,#f3f7ff 100%);border:1px solid rgba(16,35,71,0.08);box-shadow:0 20px 50px rgba(38,73,134,0.14);}
152
+ .eventus-live-preview__qr svg{display:block;width:min(220px,100%);height:auto;}
153
+ .eventus-live-preview__meta{display:none;}
154
+ @media (min-width: 720px){
155
+ .eventus-live-preview{grid-template-columns:minmax(0,1.7fr) minmax(220px,0.9fr);align-items:center;}
156
+ }
157
+ </style>
158
+ <div class="eventus-live-preview">
159
+ <div class="eventus-live-preview__copy">
160
+ <h2>Live preview from your phone</h2>
161
+ <p>Open Eventus X, go to <strong>Developer</strong>, and scan this QR code to launch the mini app with the real Eventus bridge.</p>
162
+ <dl class="eventus-live-preview__grid">
163
+ ${renderPreviewValue("Preview URL", preview.urls.preview)}
164
+ ${renderPreviewValue("Local URL", preview.urls.local)}
165
+ ${preview.urls.network ? renderPreviewValue("Network URL", preview.urls.network) : ""}
166
+ ${renderPermissions(preview.manifest.permissions)}
167
+ </dl>
168
+ <p class="eventus-live-preview__hint">Your phone and computer must be on the same local network.</p>
169
+ </div>
170
+ <div class="eventus-live-preview__qr" aria-label="Eventus live preview QR code">
171
+ ${qrSvg}
172
+ </div>
173
+ </div>
174
+ </section>`.trim();
175
+ }
176
+ async function getLivePreviewHtml() {
177
+ const preview = await fetchDevPreviewDescriptor();
178
+ if (!preview) {
179
+ return null;
180
+ }
181
+ const qrSvg = await QRCode.toString(JSON.stringify(preview), {
182
+ type: "svg",
183
+ width: 220,
184
+ margin: 1
185
+ });
186
+ return createLivePreviewHtml(preview, qrSvg);
187
+ }
188
+ function readWindowBridge() {
189
+ if (typeof window === "undefined") {
190
+ return void 0;
191
+ }
192
+ return window.Eventus;
193
+ }
194
+ function readStringField(record, key) {
195
+ const value = record?.[key];
196
+ return typeof value === "string" && value.trim() ? value : void 0;
197
+ }
198
+ function mapNativeUser(user) {
199
+ if (!user || typeof user.id !== "string" || !user.id.trim()) {
200
+ return void 0;
201
+ }
202
+ const displayName = typeof user.displayName === "string" ? user.displayName : void 0;
203
+ const name = typeof user.name === "string" ? user.name : displayName;
204
+ const avatarUrl = typeof user.avatarUrl === "string" ? user.avatarUrl : void 0;
205
+ const handle = typeof user.handle === "string" ? user.handle : void 0;
206
+ return {
207
+ id: user.id,
208
+ ...name ? { name } : {},
209
+ ...displayName ? { displayName } : {},
210
+ ...avatarUrl ? { avatarUrl } : {},
211
+ ...handle ? { handle } : {}
212
+ };
213
+ }
214
+ function mapNativeContext(bridge) {
215
+ const appId = readStringField(bridge.app, "slug") ?? readStringField(bridge.app, "id");
216
+ const locale = readStringField(bridge.device, "locale") ?? getBrowserLocale();
217
+ const themeValue = readStringField(bridge.device, "themeMode");
218
+ const theme = themeValue === "dark" ? "dark" : "light";
219
+ const user = mapNativeUser(bridge.user);
220
+ return {
221
+ locale,
222
+ theme,
223
+ ...appId ? { appId } : {},
224
+ ...user ? { user } : {}
225
+ };
226
+ }
227
+ var mockState = {
228
+ enabled: false,
229
+ context: createDefaultMockContext(),
230
+ manifestOverride: void 0,
231
+ ready: false
232
+ };
233
+ var EventusClientImpl = class {
234
+ emitter = new EventEmitter();
235
+ lastReadySource;
236
+ hasReadyRequest = false;
237
+ constructor() {
238
+ if (typeof window !== "undefined") {
239
+ window.addEventListener("eventus:ready", this.handleBridgeReady);
240
+ }
241
+ }
242
+ async ready() {
243
+ this.hasReadyRequest = true;
244
+ const bridge = await this.waitForNativeBridge();
245
+ if (bridge) {
246
+ this.emitReady(mapNativeContext(bridge), "native");
247
+ return;
248
+ }
249
+ if (!mockState.enabled) {
250
+ throw new EventusBridgeUnavailableError();
251
+ }
252
+ mockState.ready = true;
253
+ this.emitReady(mockState.context, "mock");
254
+ }
255
+ async getContext() {
256
+ const bridge = readWindowBridge();
257
+ if (bridge) {
258
+ return mapNativeContext(bridge);
259
+ }
260
+ if (!mockState.enabled) {
261
+ throw new EventusBridgeUnavailableError();
262
+ }
263
+ return mockState.context;
264
+ }
265
+ getManifest() {
266
+ const bridge = readWindowBridge();
267
+ if (bridge) {
268
+ return readInjectedManifest();
269
+ }
270
+ return mockState.manifestOverride === void 0 ? readInjectedManifest() : mockState.manifestOverride;
271
+ }
272
+ async request(action, payload) {
273
+ const bridge = readWindowBridge();
274
+ if (bridge) {
275
+ if (typeof bridge.request === "function") {
276
+ return bridge.request(action, payload);
277
+ }
278
+ const bridgeAction = bridge[action];
279
+ if (typeof bridgeAction === "function") {
280
+ return bridgeAction(payload);
281
+ }
282
+ }
283
+ if (!mockState.enabled) {
284
+ throw new EventusBridgeUnavailableError(
285
+ `Eventus request "${action}" requires a native bridge or explicit mock mode.`
286
+ );
287
+ }
288
+ const mockedResponse = {
289
+ mock: true,
290
+ action,
291
+ payload: payload ?? null,
292
+ message: "Eventus request used the browser mock fallback because no native bridge is connected."
293
+ };
294
+ return mockedResponse;
295
+ }
296
+ on(event, handler) {
297
+ return this.emitter.on(event, handler);
298
+ }
299
+ configureMock(options = {}) {
300
+ const previousTheme = mockState.context.theme;
301
+ const shouldEnableMock = !!options.context || "manifest" in options || typeof options.ready === "boolean";
302
+ if (options.reset) {
303
+ mockState.enabled = false;
304
+ mockState.context = createDefaultMockContext();
305
+ mockState.manifestOverride = void 0;
306
+ mockState.ready = false;
307
+ this.lastReadySource = void 0;
308
+ this.hasReadyRequest = false;
309
+ }
310
+ if (shouldEnableMock) {
311
+ mockState.enabled = true;
312
+ }
313
+ if (options.context) {
314
+ const nextUser = options.context.user ? {
315
+ ...mockState.context.user ?? {},
316
+ ...options.context.user
317
+ } : mockState.context.user;
318
+ mockState.context = {
319
+ ...mockState.context,
320
+ ...options.context,
321
+ ...nextUser ? { user: nextUser } : {}
322
+ };
323
+ }
324
+ if ("manifest" in options) {
325
+ mockState.manifestOverride = options.manifest;
326
+ }
327
+ if (typeof options.ready === "boolean") {
328
+ mockState.ready = options.ready;
329
+ if (!options.ready) {
330
+ this.lastReadySource = void 0;
331
+ }
332
+ }
333
+ if (mockState.context.theme !== previousTheme) {
334
+ this.emitter.emit("themeChange", {
335
+ theme: mockState.context.theme,
336
+ context: mockState.context,
337
+ source: "mock"
338
+ });
339
+ }
340
+ }
341
+ emitReady(context, source) {
342
+ if (this.lastReadySource === source) {
343
+ return;
344
+ }
345
+ this.lastReadySource = source;
346
+ this.emitter.emit("ready", {
347
+ context,
348
+ source
349
+ });
350
+ }
351
+ handleBridgeReady = () => {
352
+ if (!this.hasReadyRequest && this.lastReadySource === void 0) {
353
+ return;
354
+ }
355
+ const bridge = readWindowBridge();
356
+ if (!bridge) {
357
+ return;
358
+ }
359
+ this.emitReady(mapNativeContext(bridge), "native");
360
+ };
361
+ async waitForNativeBridge() {
362
+ const bridge = readWindowBridge();
363
+ if (bridge || typeof window === "undefined") {
364
+ return bridge;
365
+ }
366
+ return new Promise((resolve) => {
367
+ const timeout = window.setTimeout(() => {
368
+ cleanup();
369
+ resolve(readWindowBridge());
370
+ }, 120);
371
+ const onReady = () => {
372
+ cleanup();
373
+ resolve(readWindowBridge());
374
+ };
375
+ const cleanup = () => {
376
+ window.clearTimeout(timeout);
377
+ window.removeEventListener("eventus:ready", onReady);
378
+ };
379
+ window.addEventListener("eventus:ready", onReady, { once: true });
380
+ });
381
+ }
382
+ };
383
+ var eventus = new EventusClientImpl();
384
+ function configureMockEventus(options = {}) {
385
+ eventus.configureMock(options);
386
+ }
387
+
388
+ export {
389
+ EventusBridgeUnavailableError,
390
+ isBridgeUnavailableError,
391
+ getLivePreviewHtml,
392
+ eventus,
393
+ configureMockEventus
394
+ };
package/dist/index.d.ts CHANGED
@@ -19,6 +19,16 @@ type EventusManifest = {
19
19
  version: string;
20
20
  permissions?: Array<EventusPermission | string>;
21
21
  };
22
+ type EventusDevPreviewDescriptor = {
23
+ type: "eventus-dev-preview";
24
+ version: 1;
25
+ manifest: EventusManifest;
26
+ urls: {
27
+ local: string;
28
+ network: string | null;
29
+ preview: string;
30
+ };
31
+ };
22
32
  type EventusRuntimeSource = "native" | "mock";
23
33
  type EventusEventMap = {
24
34
  ready: {
@@ -49,6 +59,7 @@ type ConfigureMockEventusOptions = {
49
59
  declare class EventusBridgeUnavailableError extends Error {
50
60
  constructor(message?: string);
51
61
  }
62
+ declare function isBridgeUnavailableError(error: unknown): boolean;
52
63
  type NativeBridgeUser = Record<string, unknown>;
53
64
  type NativeBridgeRecord = Record<string, unknown>;
54
65
  type NativeEventusBridge = {
@@ -64,6 +75,7 @@ declare global {
64
75
  Eventus?: NativeEventusBridge;
65
76
  }
66
77
  }
78
+ declare function getLivePreviewHtml(): Promise<string | null>;
67
79
  declare class EventusClientImpl implements EventusClient {
68
80
  private emitter;
69
81
  private lastReadySource;
@@ -82,4 +94,4 @@ declare class EventusClientImpl implements EventusClient {
82
94
  declare const eventus: EventusClientImpl;
83
95
  declare function configureMockEventus(options?: ConfigureMockEventusOptions): void;
84
96
 
85
- export { type ConfigureMockEventusOptions, EventusBridgeUnavailableError, type EventusClient, type EventusContext, type EventusEventHandler, type EventusEventMap, type EventusEventName, type EventusManifest, type EventusPermission, type EventusRuntimeSource, type EventusTheme, type EventusUser, configureMockEventus, eventus };
97
+ export { type ConfigureMockEventusOptions, EventusBridgeUnavailableError, type EventusClient, type EventusContext, type EventusDevPreviewDescriptor, type EventusEventHandler, type EventusEventMap, type EventusEventName, type EventusManifest, type EventusPermission, type EventusRuntimeSource, type EventusTheme, type EventusUser, configureMockEventus, eventus, getLivePreviewHtml, isBridgeUnavailableError };
package/dist/index.js CHANGED
@@ -1,259 +1,14 @@
1
- // src/index.ts
2
- var EventusBridgeUnavailableError = class extends Error {
3
- constructor(message = "No Eventus bridge is connected.") {
4
- super(message);
5
- this.name = "EventusBridgeUnavailableError";
6
- }
7
- };
8
- var EventEmitter = class {
9
- listeners = /* @__PURE__ */ new Map();
10
- on(event, handler) {
11
- const nextListeners = this.listeners.get(event) ?? /* @__PURE__ */ new Set();
12
- nextListeners.add(handler);
13
- this.listeners.set(event, nextListeners);
14
- return () => {
15
- nextListeners.delete(handler);
16
- if (nextListeners.size === 0) {
17
- this.listeners.delete(event);
18
- }
19
- };
20
- }
21
- emit(event, payload) {
22
- const handlers = this.listeners.get(event);
23
- if (!handlers) {
24
- return;
25
- }
26
- for (const handler of handlers) {
27
- handler(payload);
28
- }
29
- }
30
- };
31
- function getBrowserLocale() {
32
- if (typeof navigator !== "undefined" && navigator.language) {
33
- return navigator.language;
34
- }
35
- return "en";
36
- }
37
- function createDefaultMockContext() {
38
- const manifest = readInjectedManifest();
39
- return {
40
- locale: getBrowserLocale(),
41
- theme: "light",
42
- ...manifest?.appId ? { appId: manifest.appId } : {},
43
- user: {
44
- id: "dev-user",
45
- name: "Eventus Developer"
46
- }
47
- };
48
- }
49
- function readInjectedManifest() {
50
- const manifest = globalThis.__EVENTUS_MANIFEST__;
51
- if (typeof manifest === "undefined") {
52
- return null;
53
- }
54
- return manifest ?? null;
55
- }
56
- function readWindowBridge() {
57
- if (typeof window === "undefined") {
58
- return void 0;
59
- }
60
- return window.Eventus;
61
- }
62
- function readStringField(record, key) {
63
- const value = record?.[key];
64
- return typeof value === "string" && value.trim() ? value : void 0;
65
- }
66
- function mapNativeUser(user) {
67
- if (!user || typeof user.id !== "string" || !user.id.trim()) {
68
- return void 0;
69
- }
70
- const displayName = typeof user.displayName === "string" ? user.displayName : void 0;
71
- const name = typeof user.name === "string" ? user.name : displayName;
72
- const avatarUrl = typeof user.avatarUrl === "string" ? user.avatarUrl : void 0;
73
- const handle = typeof user.handle === "string" ? user.handle : void 0;
74
- return {
75
- id: user.id,
76
- ...name ? { name } : {},
77
- ...displayName ? { displayName } : {},
78
- ...avatarUrl ? { avatarUrl } : {},
79
- ...handle ? { handle } : {}
80
- };
81
- }
82
- function mapNativeContext(bridge) {
83
- const appId = readStringField(bridge.app, "slug") ?? readStringField(bridge.app, "id");
84
- const locale = readStringField(bridge.device, "locale") ?? getBrowserLocale();
85
- const themeValue = readStringField(bridge.device, "themeMode");
86
- const theme = themeValue === "dark" ? "dark" : "light";
87
- const user = mapNativeUser(bridge.user);
88
- return {
89
- locale,
90
- theme,
91
- ...appId ? { appId } : {},
92
- ...user ? { user } : {}
93
- };
94
- }
95
- var mockState = {
96
- enabled: false,
97
- context: createDefaultMockContext(),
98
- manifestOverride: void 0,
99
- ready: false
100
- };
101
- var EventusClientImpl = class {
102
- emitter = new EventEmitter();
103
- lastReadySource;
104
- hasReadyRequest = false;
105
- constructor() {
106
- if (typeof window !== "undefined") {
107
- window.addEventListener("eventus:ready", this.handleBridgeReady);
108
- }
109
- }
110
- async ready() {
111
- this.hasReadyRequest = true;
112
- const bridge = await this.waitForNativeBridge();
113
- if (bridge) {
114
- this.emitReady(mapNativeContext(bridge), "native");
115
- return;
116
- }
117
- if (!mockState.enabled) {
118
- throw new EventusBridgeUnavailableError();
119
- }
120
- mockState.ready = true;
121
- this.emitReady(mockState.context, "mock");
122
- }
123
- async getContext() {
124
- const bridge = readWindowBridge();
125
- if (bridge) {
126
- return mapNativeContext(bridge);
127
- }
128
- if (!mockState.enabled) {
129
- throw new EventusBridgeUnavailableError();
130
- }
131
- return mockState.context;
132
- }
133
- getManifest() {
134
- const bridge = readWindowBridge();
135
- if (bridge) {
136
- return readInjectedManifest();
137
- }
138
- return mockState.manifestOverride === void 0 ? readInjectedManifest() : mockState.manifestOverride;
139
- }
140
- async request(action, payload) {
141
- const bridge = readWindowBridge();
142
- if (bridge) {
143
- if (typeof bridge.request === "function") {
144
- return bridge.request(action, payload);
145
- }
146
- const bridgeAction = bridge[action];
147
- if (typeof bridgeAction === "function") {
148
- return bridgeAction(payload);
149
- }
150
- }
151
- if (!mockState.enabled) {
152
- throw new EventusBridgeUnavailableError(
153
- `Eventus request "${action}" requires a native bridge or explicit mock mode.`
154
- );
155
- }
156
- const mockedResponse = {
157
- mock: true,
158
- action,
159
- payload: payload ?? null,
160
- message: "Eventus request used the browser mock fallback because no native bridge is connected."
161
- };
162
- return mockedResponse;
163
- }
164
- on(event, handler) {
165
- return this.emitter.on(event, handler);
166
- }
167
- configureMock(options = {}) {
168
- const previousTheme = mockState.context.theme;
169
- const shouldEnableMock = !!options.context || "manifest" in options || typeof options.ready === "boolean";
170
- if (options.reset) {
171
- mockState.enabled = false;
172
- mockState.context = createDefaultMockContext();
173
- mockState.manifestOverride = void 0;
174
- mockState.ready = false;
175
- this.lastReadySource = void 0;
176
- this.hasReadyRequest = false;
177
- }
178
- if (shouldEnableMock) {
179
- mockState.enabled = true;
180
- }
181
- if (options.context) {
182
- const nextUser = options.context.user ? {
183
- ...mockState.context.user ?? {},
184
- ...options.context.user
185
- } : mockState.context.user;
186
- mockState.context = {
187
- ...mockState.context,
188
- ...options.context,
189
- ...nextUser ? { user: nextUser } : {}
190
- };
191
- }
192
- if ("manifest" in options) {
193
- mockState.manifestOverride = options.manifest;
194
- }
195
- if (typeof options.ready === "boolean") {
196
- mockState.ready = options.ready;
197
- if (!options.ready) {
198
- this.lastReadySource = void 0;
199
- }
200
- }
201
- if (mockState.context.theme !== previousTheme) {
202
- this.emitter.emit("themeChange", {
203
- theme: mockState.context.theme,
204
- context: mockState.context,
205
- source: "mock"
206
- });
207
- }
208
- }
209
- emitReady(context, source) {
210
- if (this.lastReadySource === source) {
211
- return;
212
- }
213
- this.lastReadySource = source;
214
- this.emitter.emit("ready", {
215
- context,
216
- source
217
- });
218
- }
219
- handleBridgeReady = () => {
220
- if (!this.hasReadyRequest && this.lastReadySource === void 0) {
221
- return;
222
- }
223
- const bridge = readWindowBridge();
224
- if (!bridge) {
225
- return;
226
- }
227
- this.emitReady(mapNativeContext(bridge), "native");
228
- };
229
- async waitForNativeBridge() {
230
- const bridge = readWindowBridge();
231
- if (bridge || typeof window === "undefined") {
232
- return bridge;
233
- }
234
- return new Promise((resolve) => {
235
- const timeout = window.setTimeout(() => {
236
- cleanup();
237
- resolve(readWindowBridge());
238
- }, 120);
239
- const onReady = () => {
240
- cleanup();
241
- resolve(readWindowBridge());
242
- };
243
- const cleanup = () => {
244
- window.clearTimeout(timeout);
245
- window.removeEventListener("eventus:ready", onReady);
246
- };
247
- window.addEventListener("eventus:ready", onReady, { once: true });
248
- });
249
- }
250
- };
251
- var eventus = new EventusClientImpl();
252
- function configureMockEventus(options = {}) {
253
- eventus.configureMock(options);
254
- }
1
+ import {
2
+ EventusBridgeUnavailableError,
3
+ configureMockEventus,
4
+ eventus,
5
+ getLivePreviewHtml,
6
+ isBridgeUnavailableError
7
+ } from "./chunk-TJNC4RSJ.js";
255
8
  export {
256
9
  EventusBridgeUnavailableError,
257
10
  configureMockEventus,
258
- eventus
11
+ eventus,
12
+ getLivePreviewHtml,
13
+ isBridgeUnavailableError
259
14
  };
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { EventusContext, EventusRuntimeSource } from './index.js';
4
+
5
+ type EventusReactContextValue = {
6
+ context: EventusContext;
7
+ runtimeSource: EventusRuntimeSource;
8
+ };
9
+ type EventusContextProviderProps = {
10
+ children: ReactNode;
11
+ bridgeUnavailable?: ReactNode;
12
+ };
13
+ declare function EventusContextProvider({ children, bridgeUnavailable, }: EventusContextProviderProps): react_jsx_runtime.JSX.Element;
14
+ declare function useEventusContext(): EventusReactContextValue;
15
+
16
+ export { EventusContextProvider, type EventusContextProviderProps, useEventusContext };
package/dist/react.js ADDED
@@ -0,0 +1,182 @@
1
+ import {
2
+ eventus,
3
+ getLivePreviewHtml,
4
+ isBridgeUnavailableError
5
+ } from "./chunk-TJNC4RSJ.js";
6
+
7
+ // src/react.tsx
8
+ import {
9
+ createContext,
10
+ useContext,
11
+ useEffect,
12
+ useState
13
+ } from "react";
14
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
+ var EventusReactContext = createContext(
16
+ null
17
+ );
18
+ function EventusContextProvider({
19
+ children,
20
+ bridgeUnavailable
21
+ }) {
22
+ const [state, setState] = useState({ status: "loading" });
23
+ useEffect(() => {
24
+ let cancelled = false;
25
+ let receivedReadyEvent = false;
26
+ const unsubscribeReady = eventus.on("ready", ({ context, source }) => {
27
+ receivedReadyEvent = true;
28
+ if (!cancelled) {
29
+ setState({
30
+ status: "ready",
31
+ context,
32
+ runtimeSource: source
33
+ });
34
+ }
35
+ });
36
+ const unsubscribeThemeChange = eventus.on(
37
+ "themeChange",
38
+ ({ context, source }) => {
39
+ if (!cancelled) {
40
+ setState({
41
+ status: "ready",
42
+ context,
43
+ runtimeSource: source
44
+ });
45
+ }
46
+ }
47
+ );
48
+ void (async () => {
49
+ try {
50
+ await eventus.ready();
51
+ const context = await eventus.getContext();
52
+ if (!cancelled && !receivedReadyEvent) {
53
+ setState({
54
+ status: "ready",
55
+ context,
56
+ runtimeSource: window.Eventus ? "native" : "mock"
57
+ });
58
+ }
59
+ } catch (error) {
60
+ if (receivedReadyEvent || cancelled) {
61
+ return;
62
+ }
63
+ if (isBridgeUnavailableError(error)) {
64
+ const previewHtml = await getLivePreviewHtml();
65
+ if (!cancelled && !receivedReadyEvent) {
66
+ setState({
67
+ status: "bridge-unavailable",
68
+ previewHtml
69
+ });
70
+ }
71
+ return;
72
+ }
73
+ if (!cancelled) {
74
+ setState({
75
+ status: "error",
76
+ message: error instanceof Error ? error.message : "Unable to initialize the Eventus client."
77
+ });
78
+ }
79
+ }
80
+ })();
81
+ return () => {
82
+ cancelled = true;
83
+ unsubscribeReady();
84
+ unsubscribeThemeChange();
85
+ };
86
+ }, []);
87
+ if (state.status === "ready") {
88
+ return /* @__PURE__ */ jsx(
89
+ EventusReactContext.Provider,
90
+ {
91
+ value: {
92
+ context: state.context,
93
+ runtimeSource: state.runtimeSource
94
+ },
95
+ children
96
+ }
97
+ );
98
+ }
99
+ if (state.status === "bridge-unavailable") {
100
+ if (bridgeUnavailable) {
101
+ return /* @__PURE__ */ jsx(Fragment, { children: bridgeUnavailable });
102
+ }
103
+ return /* @__PURE__ */ jsx(DefaultBridgeUnavailable, { previewHtml: state.previewHtml });
104
+ }
105
+ if (state.status === "error") {
106
+ return /* @__PURE__ */ jsx(DefaultStatusCard, { title: "Unable to start Eventus", body: state.message });
107
+ }
108
+ return /* @__PURE__ */ jsx(
109
+ DefaultStatusCard,
110
+ {
111
+ title: "Connecting to Eventus",
112
+ body: "Waiting for the Eventus bridge to become available."
113
+ }
114
+ );
115
+ }
116
+ function useEventusContext() {
117
+ const value = useContext(EventusReactContext);
118
+ if (!value) {
119
+ throw new Error(
120
+ "useEventusContext must be used within an EventusContextProvider."
121
+ );
122
+ }
123
+ return value;
124
+ }
125
+ function DefaultBridgeUnavailable({
126
+ previewHtml
127
+ }) {
128
+ if (previewHtml) {
129
+ return /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: previewHtml } });
130
+ }
131
+ return /* @__PURE__ */ jsx(
132
+ DefaultStatusCard,
133
+ {
134
+ title: "Open inside Eventus X",
135
+ body: "This mini app needs the Eventus bridge to access the real runtime. Open it from Eventus X to test it with production-like behavior."
136
+ }
137
+ );
138
+ }
139
+ function DefaultStatusCard({ title, body }) {
140
+ return /* @__PURE__ */ jsx("main", { style: statusShellStyle, children: /* @__PURE__ */ jsxs("section", { style: statusCardStyle, children: [
141
+ /* @__PURE__ */ jsx("p", { style: statusEyebrowStyle, children: "Eventus Mini App Toolkit" }),
142
+ /* @__PURE__ */ jsx("h1", { style: statusTitleStyle, children: title }),
143
+ /* @__PURE__ */ jsx("p", { style: statusBodyStyle, children: body })
144
+ ] }) });
145
+ }
146
+ var statusShellStyle = {
147
+ minHeight: "100vh",
148
+ display: "grid",
149
+ placeItems: "center",
150
+ padding: "24px",
151
+ background: "radial-gradient(circle at top, #d8f2ff 0%, #f3f9ff 35%, #eef3ff 100%)"
152
+ };
153
+ var statusCardStyle = {
154
+ width: "min(680px, 100%)",
155
+ padding: "32px",
156
+ borderRadius: "24px",
157
+ background: "rgba(255, 255, 255, 0.88)",
158
+ border: "1px solid rgba(16, 35, 71, 0.08)",
159
+ boxShadow: "0 24px 80px rgba(38, 73, 134, 0.14)",
160
+ color: "#102347"
161
+ };
162
+ var statusEyebrowStyle = {
163
+ margin: "0 0 8px",
164
+ fontSize: "0.85rem",
165
+ letterSpacing: "0.12em",
166
+ textTransform: "uppercase",
167
+ color: "#3f6ea8"
168
+ };
169
+ var statusTitleStyle = {
170
+ margin: "0 0 12px",
171
+ fontSize: "clamp(2rem, 4vw, 3rem)",
172
+ lineHeight: 1.05
173
+ };
174
+ var statusBodyStyle = {
175
+ margin: 0,
176
+ color: "#465d82",
177
+ lineHeight: 1.6
178
+ };
179
+ export {
180
+ EventusContextProvider,
181
+ useEventusContext
182
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventusgo/sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Eventus mini-app SDK with an explicit browser mock mode.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,14 +13,31 @@
13
13
  ".": {
14
14
  "types": "./dist/index.d.ts",
15
15
  "import": "./dist/index.js"
16
+ },
17
+ "./react": {
18
+ "types": "./dist/react.d.ts",
19
+ "import": "./dist/react.js"
16
20
  }
17
21
  },
18
22
  "files": [
19
23
  "dist"
20
24
  ],
21
25
  "scripts": {
22
- "build": "tsup src/index.ts --format esm --dts --clean",
26
+ "build": "tsup src/index.ts src/react.tsx --format esm --dts --clean",
23
27
  "check": "tsc -p tsconfig.json --noEmit",
24
28
  "test": "vitest run"
29
+ },
30
+ "dependencies": {
31
+ "qrcode": "1.5.4"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "^18.0.0 || ^19.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/qrcode": "1.5.5",
38
+ "@types/react": "18.3.28",
39
+ "@types/react-dom": "18.3.7",
40
+ "react": "18.3.1",
41
+ "react-dom": "18.3.1"
25
42
  }
26
43
  }