@nominalso/vibe-host 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,19 @@
1
1
  // package.json
2
- var version = "0.1.0";
2
+ var version = "0.3.0";
3
3
 
4
4
  // ../protocol-types/dist/index.js
5
+ function normalizeSubroute(subroute) {
6
+ const normalized = subroute.startsWith("/") ? subroute : `/${subroute}`;
7
+ const path = normalized.split(/[?#]/)[0];
8
+ let decoded = path;
9
+ let prev;
10
+ do {
11
+ prev = decoded;
12
+ decoded = decoded.replace(/%25/gi, "%").replace(/%2e/gi, ".").replace(/%2f/gi, "/");
13
+ } while (decoded !== prev);
14
+ if (decoded.split("/").some((segment) => segment === "..")) return null;
15
+ return normalized;
16
+ }
5
17
  var PROTOCOL_ID = "nominal-vibe-bridge";
6
18
  var PROTOCOL_VERSION = 1;
7
19
  var BRIDGE_KINDS = ["request", "response", "push", "command", "progress"];
@@ -18,6 +30,47 @@ function isBridgeMessage(data) {
18
30
  if (CORRELATED_KINDS_SET.has(d.kind) && typeof d.requestId !== "string") return false;
19
31
  return true;
20
32
  }
33
+ var BridgeError = class extends Error {
34
+ constructor(code, message) {
35
+ super(message);
36
+ this.code = code;
37
+ this.name = "BridgeError";
38
+ }
39
+ };
40
+ var HttpBridgeError = class extends BridgeError {
41
+ constructor(status, message) {
42
+ super("REQUEST_FAILED", message);
43
+ this.status = status;
44
+ this.name = "HttpBridgeError";
45
+ }
46
+ };
47
+ function isOriginPattern(entry) {
48
+ return entry.includes("*");
49
+ }
50
+ var patternCache = /* @__PURE__ */ new Map();
51
+ function patternToRegExp(pattern) {
52
+ const cached = patternCache.get(pattern);
53
+ if (cached) return cached;
54
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55
+ const body = escaped.replace(/\\\*/g, "[^./:@?#\\s]+");
56
+ const compiled = new RegExp(`^${body}$`);
57
+ patternCache.set(pattern, compiled);
58
+ return compiled;
59
+ }
60
+ function matchesOrigin(origin, entry) {
61
+ if (!isOriginPattern(entry)) return origin === entry;
62
+ return patternToRegExp(entry).test(origin);
63
+ }
64
+ function isOriginAllowed(origin, allowlist, { allowPatterns }) {
65
+ for (const entry of allowlist) {
66
+ if (isOriginPattern(entry)) {
67
+ if (allowPatterns && matchesOrigin(origin, entry)) return true;
68
+ } else if (origin === entry) {
69
+ return true;
70
+ }
71
+ }
72
+ return false;
73
+ }
21
74
 
22
75
  // ../api-client/dist/index.js
23
76
  import {
@@ -352,10 +405,6 @@ var getSubsidiaryParentCurrenciesApiTenancySubsidiarySubsidiaryIdParentCurrencie
352
405
  };
353
406
  function createVibeApiClient({ baseUrl, stripApiPrefix }) {
354
407
  const client2 = createClient2(createConfig2({ baseUrl }));
355
- client2.interceptors.request.use((request) => {
356
- console.log("[vibe-api-client] request url:", request.url);
357
- return request;
358
- });
359
408
  if (stripApiPrefix) {
360
409
  client2.interceptors.request.use(
361
410
  (request) => new Request(request.url.replace(`${baseUrl}/api/`, `${baseUrl}/`), request)
@@ -364,16 +413,28 @@ function createVibeApiClient({ baseUrl, stripApiPrefix }) {
364
413
  return client2;
365
414
  }
366
415
 
367
- // src/handlerMap.ts
416
+ // src/handlers/call.ts
368
417
  async function call(fn, payload, client2) {
369
- const { data } = await fn({ ...payload, client: client2 });
370
- if (data == null) throw new Error("API returned no data");
418
+ const { data, response } = await fn({ ...payload, client: client2 });
419
+ if (data == null) {
420
+ if (response && !response.ok) {
421
+ throw new HttpBridgeError(
422
+ response.status,
423
+ `API request failed with status ${response.status}`
424
+ );
425
+ }
426
+ if (!response) {
427
+ throw new Error("API request produced no response");
428
+ }
429
+ return null;
430
+ }
371
431
  return data;
372
432
  }
373
- function createHandlerMap(options) {
374
- const client2 = createVibeApiClient(options);
433
+
434
+ // src/handlers/accounting.ts
435
+ function accountingHandlers(client2) {
375
436
  return {
376
- // Accounting — chart of accounts
437
+ // Chart of accounts
377
438
  GET_CHART_OF_ACCOUNTS: (p) => call(getCoaFlatApiAccountingSubsidiarySubsidiaryIdCoaAccountGet, p, client2),
378
439
  GET_COA_TREE: (p) => call(getCoaTreeApiAccountingSubsidiarySubsidiaryIdCoaGet, p, client2),
379
440
  GET_COA_FLAT_SIMPLE: (p) => call(
@@ -398,7 +459,7 @@ function createHandlerMap(options) {
398
459
  ),
399
460
  GET_ACCOUNTS: (p) => call(getAccountsApiAccountingAccountGet, p, client2),
400
461
  GET_ACCOUNT: (p) => call(getAccountApiAccountingAccountAccountIdGet, p, client2),
401
- // Accounting — exchange rates
462
+ // Exchange rates
402
463
  GET_CONVERSION_RATES: (p) => call(getConversionRateApiAccountingCurrencyCurrencyConversionRatesGet, p, client2),
403
464
  GET_EFFECTIVE_EXCHANGE_RATE: (p) => call(getEffectiveExchangeRateApiAccountingConsolidationExchangeRateGet, p, client2),
404
465
  GET_EXCHANGE_RATE_BY_DATE: (p) => call(
@@ -406,7 +467,7 @@ function createHandlerMap(options) {
406
467
  p,
407
468
  client2
408
469
  ),
409
- // Accounting — dimensions
470
+ // Dimensions
410
471
  GET_DIMENSIONS: (p) => call(getDimensionsApiAccountingDimensionGet, p, client2),
411
472
  GET_DIMENSION_VALUES: (p) => call(getDimensionValuesApiAccountingDimensionValueGet, p, client2),
412
473
  GET_DIMENSION_VALUES_HIERARCHICAL: (p) => call(
@@ -419,7 +480,7 @@ function createHandlerMap(options) {
419
480
  p,
420
481
  client2
421
482
  ),
422
- // Accounting — journal entries
483
+ // Journal entries
423
484
  GET_JOURNAL_ENTRIES: (p) => call(
424
485
  getSubsidiaryJournalApiAccountingSubsidiarySubsidiaryIdJournalEntryGet,
425
486
  p,
@@ -434,8 +495,14 @@ function createHandlerMap(options) {
434
495
  getJournalEntryApiAccountingSubsidiarySubsidiaryIdJournalEntryJournalEntryIdGet,
435
496
  p,
436
497
  client2
437
- ),
438
- // Activity — period instances
498
+ )
499
+ };
500
+ }
501
+
502
+ // src/handlers/activity.ts
503
+ function activityHandlers(client2) {
504
+ return {
505
+ // Period instances
439
506
  GET_PERIODS: (p) => call(getPeriodInstancesApiActivityPeriodInstanceGet, p, client2),
440
507
  GET_PERIOD_INSTANCE: (p) => call(getPeriodInstanceApiActivityPeriodInstancePeriodInstanceIdGet, p, client2),
441
508
  GET_PERIOD_INSTANCE_BY_SLUG: (p) => call(
@@ -448,14 +515,14 @@ function createHandlerMap(options) {
448
515
  p,
449
516
  client2
450
517
  ),
451
- // Activity — activity definitions
518
+ // Activity definitions
452
519
  GET_ACTIVITY_DEFINITIONS: (p) => call(getActivityDefinitionsApiActivityActivityDefinitionGet, p, client2),
453
520
  GET_ACTIVITY_DEFINITION: (p) => call(
454
521
  getActivityDefinitionByIdApiActivityActivityDefinitionActivityDefinitionIdGet,
455
522
  p,
456
523
  client2
457
524
  ),
458
- // Activity — activity instances
525
+ // Activity instances
459
526
  GET_ACTIVITY_INSTANCES: (p) => call(getActivityInstancesApiActivityActivityInstanceGet, p, client2),
460
527
  GET_ACTIVITY_INSTANCE: (p) => call(getActivityInstanceApiActivityActivityInstanceActivityInstanceIdGet, p, client2),
461
528
  GET_ACTIVITY_INSTANCE_BY_PERIOD: (p) => call(
@@ -473,28 +540,43 @@ function createHandlerMap(options) {
473
540
  p,
474
541
  client2
475
542
  ),
476
- // Activity — task definitions
543
+ // Task definitions
477
544
  GET_TASK_DEFINITIONS: (p) => call(getAllTasksDefinitionsApiActivityTaskDefinitionGet, p, client2),
478
545
  GET_TASK_DEFINITION: (p) => call(getTaskDefinitionByIdApiActivityTaskDefinitionTaskDefinitionIdGet, p, client2),
479
546
  CREATE_TASK_DEFINITION: (p) => call(createTaskDefinitionApiActivityTaskDefinitionPost, p, client2),
480
547
  UPDATE_TASK_DEFINITION: (p) => call(updateTaskDefinitionApiActivityTaskDefinitionTaskDefinitionIdPut, p, client2),
481
548
  GET_TASK_DEFINITIONS_BY_FILTER: (p) => call(getAllTasksDefinitionsByFilterApiActivityTaskDefinitionQueryPost, p, client2),
482
- // Activity — task instances
549
+ // Task instances
483
550
  GET_TASK_INSTANCES: (p) => call(getAllTaskInstancesApiActivityTaskInstanceGet, p, client2),
484
551
  GET_TASK_INSTANCE: (p) => call(getTaskInstanceByIdApiActivityTaskInstanceTaskInstanceIdGet, p, client2),
485
552
  POST_TASK_OUTPUT: (p) => call(updateTaskInstanceApiActivityTaskInstanceTaskInstanceIdPut, p, client2),
486
- GET_TASK_INSTANCES_BY_FILTER: (p) => call(getAllTaskInstancesByFilterApiActivityTaskInstanceQueryPost, p, client2),
487
- // Audit trail
553
+ GET_TASK_INSTANCES_BY_FILTER: (p) => call(getAllTaskInstancesByFilterApiActivityTaskInstanceQueryPost, p, client2)
554
+ };
555
+ }
556
+
557
+ // src/handlers/audit-trail.ts
558
+ function auditTrailHandlers(client2) {
559
+ return {
488
560
  GET_AUDIT_EVENTS: (p) => call(getEventsApiAuditTrailEventGet, p, client2),
489
- GET_ENTITY_AUDIT_EVENTS: (p) => call(getEntityEventsApiAuditTrailEventEntityTargetTypeTargetIdPost, p, client2),
490
- // Period manager
561
+ GET_ENTITY_AUDIT_EVENTS: (p) => call(getEntityEventsApiAuditTrailEventEntityTargetTypeTargetIdPost, p, client2)
562
+ };
563
+ }
564
+
565
+ // src/handlers/period-manager.ts
566
+ function periodManagerHandlers(client2) {
567
+ return {
491
568
  GET_FISCAL_CALENDARS: (p) => call(
492
569
  getFiscalCalendarsBySubsidiaryApiPeriodManagerFiscalCalendarBySubsidiarySubsidiaryIdGet,
493
570
  p,
494
571
  client2
495
572
  ),
496
- GET_FISCAL_CALENDAR: (p) => call(getFiscalCalendarByIdApiPeriodManagerFiscalCalendarCalendarIdGet, p, client2),
497
- // Tenancy
573
+ GET_FISCAL_CALENDAR: (p) => call(getFiscalCalendarByIdApiPeriodManagerFiscalCalendarCalendarIdGet, p, client2)
574
+ };
575
+ }
576
+
577
+ // src/handlers/tenancy.ts
578
+ function tenancyHandlers(client2) {
579
+ return {
498
580
  GET_SUBSIDIARIES: (p) => call(getAllSubsidiariesApiTenancySubsidiaryGet, p, client2),
499
581
  GET_SUBSIDIARY: (p) => call(getSubsidiaryApiTenancySubsidiarySubsidiaryIdGet, p, client2),
500
582
  GET_SUBSIDIARY_PARENT_CURRENCIES: (p) => call(
@@ -506,46 +588,122 @@ function createHandlerMap(options) {
506
588
  };
507
589
  }
508
590
 
509
- // src/uploadHandler.ts
510
- async function uploadHandler(payload, sendProgress) {
511
- const attachmentId = crypto.randomUUID();
512
- for (const progress of [25, 50, 75, 100]) {
513
- await new Promise((r) => setTimeout(r, 400));
514
- sendProgress({ attachmentId, progress });
591
+ // src/handlerMap.ts
592
+ function createHandlerMap(options) {
593
+ const client2 = createVibeApiClient(options);
594
+ return {
595
+ ...accountingHandlers(client2),
596
+ ...activityHandlers(client2),
597
+ ...auditTrailHandlers(client2),
598
+ ...periodManagerHandlers(client2),
599
+ ...tenancyHandlers(client2)
600
+ };
601
+ }
602
+
603
+ // src/rateLimiter.ts
604
+ var DEFAULT_RATE_LIMIT = 60;
605
+ function createRateLimiter(limit = DEFAULT_RATE_LIMIT, windowMs = 6e4) {
606
+ return { timestamps: [], limit, windowMs };
607
+ }
608
+ function checkRateLimit(state) {
609
+ const now = Date.now();
610
+ const cutoff = now - state.windowMs;
611
+ state.timestamps = state.timestamps.filter((t) => t > cutoff);
612
+ if (state.timestamps.length >= state.limit) {
613
+ return false;
515
614
  }
516
- return { attachmentId, name: payload.fileName };
615
+ state.timestamps.push(now);
616
+ return true;
617
+ }
618
+
619
+ // src/unwired.ts
620
+ function warnUnwired(command) {
621
+ return () => {
622
+ console.warn(`[vibe-host] Received "${command}" command but no handler is wired \u2014 ignoring.`);
623
+ };
624
+ }
625
+ function rejectUnwired(op) {
626
+ return () => Promise.reject(
627
+ new BridgeError("NOT_WIRED", `[vibe-host] Received "${op}" request but no handler is wired.`)
628
+ );
517
629
  }
518
630
 
519
631
  // src/VibeAppHost.ts
520
632
  var VibeAppHost = class {
521
- constructor({ trustedOrigins, getContext, appBasePath, clientConfig }) {
633
+ constructor(opts) {
634
+ /**
635
+ * Concrete iframe origins observed on validated inbound messages. Because you
636
+ * cannot `postMessage` to a glob pattern, proactive pushes target these
637
+ * learned origins (plus the exact allowlist entries) rather than the patterns.
638
+ */
639
+ this.connectedOrigins = /* @__PURE__ */ new Set();
522
640
  this.iframeWindow = null;
641
+ /**
642
+ * Source windows we've already logged a successful handshake for. The bridge
643
+ * polls `GET_CONTEXT` on an interval until it connects, so without this the
644
+ * "Vibe App connected" line repeats on every poll (SSOT §13 F3).
645
+ */
646
+ this.handshakeLoggedWindows = /* @__PURE__ */ new WeakSet();
523
647
  this.kindHandlers = {
524
648
  request: (msg, source, origin) => this.handleRequest(msg, source, origin),
525
649
  command: (msg) => this.dispatchCommand(msg)
526
650
  };
527
- this.commandHandlers = {
528
- REPORT_SUBROUTE: (payload) => {
529
- const newPath = payload.subroute === "/" ? this.appBasePath : `${this.appBasePath}${payload.subroute}`;
530
- const method = payload.replace ? "replaceState" : "pushState";
531
- window.history[method](null, "", newPath);
532
- }
533
- };
651
+ const { trustedOrigins, getContext, appBasePath, clientConfig } = opts;
534
652
  this.trustedOrigins = new Set(trustedOrigins);
653
+ this.exactOrigins = trustedOrigins.filter((entry) => !isOriginPattern(entry));
535
654
  this.getContext = getContext;
536
655
  this.appBasePath = appBasePath;
656
+ this.onRequestComplete = opts.onRequestComplete;
657
+ this.rateLimiter = createRateLimiter(opts.rateLimit ?? DEFAULT_RATE_LIMIT);
537
658
  this.boundHandleMessage = this.handleMessage.bind(this);
538
659
  this.boundHandlePopState = this.handlePopState.bind(this);
539
- this.handlers = this.initializeHandlers(clientConfig || { baseUrl: "", stripApiPrefix: false });
660
+ this.handlers = this.initializeHandlers(
661
+ clientConfig || { baseUrl: "", stripApiPrefix: false },
662
+ opts
663
+ );
664
+ this.commandHandlers = this.buildCommandHandlers(opts);
540
665
  }
541
666
  dispatchCommand(msg) {
542
667
  const handler = this.commandHandlers[msg.type];
543
- handler(msg.payload);
668
+ if (!handler) {
669
+ console.warn(`[vibe-host] Received unknown command "${msg.type}" \u2014 ignoring.`);
670
+ return;
671
+ }
672
+ try {
673
+ handler(msg.payload);
674
+ } catch (err) {
675
+ console.warn(`[vibe-host] Command "${msg.type}" handler threw \u2014 ignoring.`, err);
676
+ }
544
677
  }
545
- initializeHandlers(clientConfig) {
678
+ /**
679
+ * Commands: `REPORT_SUBROUTE` is handled internally (transport + traversal
680
+ * guard, item `l`); navigation/UI side effects are injected by the consumer,
681
+ * falling back to warn-and-ignore (DP4).
682
+ */
683
+ buildCommandHandlers(opts) {
684
+ return {
685
+ REPORT_SUBROUTE: (payload) => {
686
+ const safe = normalizeSubroute(payload.subroute);
687
+ if (safe === null) return;
688
+ const newPath = safe === "/" ? this.appBasePath : `${this.appBasePath}${safe}`;
689
+ const method = payload.replace ? "replaceState" : "pushState";
690
+ window.history[method](null, "", newPath);
691
+ },
692
+ NAVIGATE: opts.onNavigate ?? warnUnwired("NAVIGATE"),
693
+ SET_SIDE_NAV: opts.onSetSideNav ?? warnUnwired("SET_SIDE_NAV"),
694
+ SWITCH_SUBSIDIARY: opts.onSwitchSubsidiary ?? warnUnwired("SWITCH_SUBSIDIARY")
695
+ };
696
+ }
697
+ /**
698
+ * Requests: the built-in API map (hey-api), plus the injected/awaited side
699
+ * effects with safe defaults — unwired `UPLOAD_FILE` rejects with a coded
700
+ * error; unwired `INVALIDATE_CACHE` resolves to a no-op (DP4).
701
+ */
702
+ initializeHandlers(clientConfig, opts) {
546
703
  return {
547
704
  ...createHandlerMap(clientConfig),
548
- UPLOAD_FILE: uploadHandler
705
+ UPLOAD_FILE: opts.onUpload ?? rejectUnwired("UPLOAD_FILE"),
706
+ INVALIDATE_CACHE: opts.onInvalidateCache ?? (async () => ({ invalidated: false }))
549
707
  };
550
708
  }
551
709
  /**
@@ -566,6 +724,7 @@ var VibeAppHost = class {
566
724
  window.removeEventListener("message", this.boundHandleMessage);
567
725
  window.removeEventListener("popstate", this.boundHandlePopState);
568
726
  this.iframeWindow = null;
727
+ this.connectedOrigins.clear();
569
728
  };
570
729
  }
571
730
  /**
@@ -585,7 +744,7 @@ var VibeAppHost = class {
585
744
  type: "CONTEXT_PUSH",
586
745
  payload: { ...this.getContext(), hostVersion: version }
587
746
  };
588
- for (const origin of this.trustedOrigins) {
747
+ for (const origin of this.concreteTargets()) {
589
748
  target.postMessage(message, origin);
590
749
  }
591
750
  }
@@ -605,24 +764,55 @@ var VibeAppHost = class {
605
764
  type,
606
765
  payload
607
766
  };
608
- for (const origin of this.trustedOrigins) {
767
+ for (const origin of this.concreteTargets()) {
609
768
  this.iframeWindow.postMessage(message, origin);
610
769
  }
611
770
  }
612
771
  async handleMessage(event) {
613
- if (!this.trustedOrigins.has(event.origin)) return;
772
+ if (!isOriginAllowed(event.origin, this.trustedOrigins, { allowPatterns: true })) return;
614
773
  if (!isBridgeMessage(event.data)) return;
774
+ this.connectedOrigins.add(event.origin);
615
775
  await this.kindHandlers[event.data.kind]?.(event.data, event.source, event.origin);
616
776
  }
777
+ /**
778
+ * Concrete origins a proactive push may target: the exact allowlist entries
779
+ * plus any origins seen on validated inbound messages. Never includes glob
780
+ * patterns (you cannot `postMessage` to one). When only exact origins are
781
+ * configured this equals the configured set, so behaviour is unchanged.
782
+ */
783
+ concreteTargets() {
784
+ return /* @__PURE__ */ new Set([...this.exactOrigins, ...this.connectedOrigins]);
785
+ }
617
786
  async handleRequest(msg, source, origin) {
618
787
  const { requestId, type } = msg;
619
788
  if (type === "GET_CONTEXT") {
620
- const ctx = { ...this.getContext(), hostVersion: version };
621
- const bridgeVersion = msg.payload?.bridgeVersion ?? "unknown";
622
- console.log(
623
- `[vibe-host] Vibe App connected \u2014 bridge: ${bridgeVersion}, host: ${version}, tenant: ${ctx.tenant}, user: ${ctx.user.displayName}`
624
- );
625
- this.respond(source, origin, { kind: "response", type, requestId, ok: true, data: ctx });
789
+ try {
790
+ const ctx = { ...this.getContext(), hostVersion: version };
791
+ if (!this.handshakeLoggedWindows.has(source)) {
792
+ this.handshakeLoggedWindows.add(source);
793
+ const bridgeVersion = msg.payload?.bridgeVersion ?? "unknown";
794
+ console.log(
795
+ `[vibe-host] Vibe App connected \u2014 bridge: ${bridgeVersion}, host: ${version}, tenant: ${ctx.tenant}, user: ${ctx.user.displayName}`
796
+ );
797
+ }
798
+ this.respond(source, origin, { kind: "response", type, requestId, ok: true, data: ctx });
799
+ } catch (err) {
800
+ const error = err instanceof Error ? err.message : String(err);
801
+ const code = err instanceof BridgeError ? err.code : "CONTEXT_ERROR";
802
+ this.respond(source, origin, { kind: "response", type, requestId, ok: false, error, code });
803
+ }
804
+ return;
805
+ }
806
+ if (!checkRateLimit(this.rateLimiter)) {
807
+ this.respond(source, origin, {
808
+ kind: "response",
809
+ type,
810
+ requestId,
811
+ ok: false,
812
+ error: `Too many requests. Limit is ${this.rateLimiter.limit} per minute.`,
813
+ code: "RATE_LIMITED"
814
+ });
815
+ this.onRequestComplete?.({ type, ok: false, payload: msg.payload });
626
816
  return;
627
817
  }
628
818
  const sendProgress = (payload) => {
@@ -640,9 +830,21 @@ var VibeAppHost = class {
640
830
  const handler = this.handlers[type];
641
831
  const data = await handler(msg.payload, sendProgress);
642
832
  this.respond(source, origin, { kind: "response", type, requestId, ok: true, data });
833
+ this.onRequestComplete?.({ type, ok: true, payload: msg.payload });
643
834
  } catch (err) {
644
835
  const error = err instanceof Error ? err.message : String(err);
645
- this.respond(source, origin, { kind: "response", type, requestId, ok: false, error });
836
+ const code = err instanceof BridgeError ? err.code : void 0;
837
+ const status = err instanceof HttpBridgeError ? err.status : void 0;
838
+ this.respond(source, origin, {
839
+ kind: "response",
840
+ type,
841
+ requestId,
842
+ ok: false,
843
+ error,
844
+ code,
845
+ status
846
+ });
847
+ this.onRequestComplete?.({ type, ok: false, payload: msg.payload });
646
848
  }
647
849
  }
648
850
  respond(target, origin, response) {
@@ -653,5 +855,7 @@ var VibeAppHost = class {
653
855
  }
654
856
  };
655
857
  export {
858
+ BridgeError,
859
+ HttpBridgeError,
656
860
  VibeAppHost
657
861
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nominalso/vibe-host",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Host-side SDK for embedding Nominal Vibe Apps — receives bridge requests over a typed postMessage protocol and dispatches them to Nominal APIs (used by nom-ui).",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://github.com/nominalso/vibe-apps-sdk#readme",