@agentshouse/app-bridge 0.1.0-alpha.3 → 0.1.0-alpha.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.
package/README.md CHANGED
@@ -13,7 +13,11 @@ port's one listener. It carries four families:
13
13
  returns there when it completes. An App names an address House gave it in a
14
14
  reading and composes none.
15
15
  - **Status** - the App reference the Runtime port is attached under, sync
16
- state, credential state, release availability and observed failure.
16
+ state, credential state, release availability and observed failure, and
17
+ `ga`: whether the page measures and the visitor's stored analytics choice.
18
+ The App reports the visitor's choice with `gaConsent('accept')` or
19
+ `gaConsent('decline')`; the shell hands it to House's analytics loader, and
20
+ the choice never reaches House.
17
21
  - **Site image read** - given a Published Room and an original attachment
18
22
  reference, it answers the web copy's path and metadata, or `null` when no
19
23
  copy exists. The original bytes never cross this bridge at a Site.
@@ -71,4 +75,6 @@ slice below the App's path, admitting nothing, so it delivers `//notes` exactly
71
75
  as a browser at `/apps/<app>//notes` does. It records every push and replace
72
76
  and answers each with the address the shell would then show, records an
73
77
  admitted ceremony, refuses a push, replace or ceremony that is not a
74
- same-origin absolute path, publishes a status, and attaches a Runtime port.
78
+ same-origin absolute path, publishes a status, records in `consents` each
79
+ `gaConsent` it takes while that status measures and publishes the status the
80
+ choice leaves, and attaches a Runtime port.
@@ -1,4 +1,4 @@
1
- import type { HouseBridgePort, HouseMessagePort, HouseStatus } from './wire.js';
1
+ import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseStatus } from './wire.js';
2
2
  export declare class HouseCeremonyRefused extends Error {
3
3
  readonly path: string | null;
4
4
  constructor(path: string | null);
@@ -23,5 +23,6 @@ export interface HouseBridge {
23
23
  readAttachment(room: string, attachment: string): Promise<HouseImageCopy | null>;
24
24
  status(): HouseStatus | null;
25
25
  onStatus(listener: (status: HouseStatus) => void): () => void;
26
+ gaConsent(choice: HouseGaChoice): void;
26
27
  }
27
28
  export declare function connectHouseBridge(source?: Promise<HouseBridgePort>): HouseBridge;
@@ -150,5 +150,8 @@ export function connectHouseBridge(source = heldBridge()) {
150
150
  statusListeners.delete(listener);
151
151
  };
152
152
  },
153
+ gaConsent(choice) {
154
+ send({ house: 'ga/consent', choice });
155
+ },
153
156
  };
154
157
  }
@@ -1,3 +1,3 @@
1
1
  export { connectHouseBridge, HouseCeremonyRefused, HouseNavigationRefused, type HouseBridge, type HouseImageCopy, } from './bridge.js';
2
2
  export { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
3
- export type { HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseMessagePort, HouseNavigationMode, HouseStatus, HouseSyncState, } from './wire.js';
3
+ export type { HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseGa, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseStatus, HouseSyncState, } from './wire.js';
@@ -1,5 +1,5 @@
1
1
  import type { HouseImageCopy } from './bridge.js';
2
- import type { HouseBridgePort, HouseMessagePort, HouseNavigationMode, HouseStatus } from './wire.js';
2
+ import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseStatus } from './wire.js';
3
3
  export interface HouseBridgeNavigation {
4
4
  readonly mode: HouseNavigationMode;
5
5
  readonly address: string;
@@ -8,6 +8,7 @@ export interface HouseBridgeTestHost {
8
8
  readonly port: Promise<HouseBridgePort>;
9
9
  readonly navigations: readonly HouseBridgeNavigation[];
10
10
  readonly ceremonies: readonly string[];
11
+ readonly consents: readonly HouseGaChoice[];
11
12
  address(): string;
12
13
  open(address: string): void;
13
14
  publish(status: HouseStatus): void;
@@ -14,10 +14,12 @@ function sameOriginPath(value) {
14
14
  export function createHouseBridgeTestHost(options = {}) {
15
15
  const navigations = [];
16
16
  const ceremonies = [];
17
+ const consents = [];
17
18
  const listeners = new Set();
18
19
  const queued = [];
19
20
  let started = false;
20
21
  let address = appAddressOf('/', options.address ?? '/');
22
+ let status = options.status ?? null;
21
23
  function deliver(data, ports = []) {
22
24
  const event = { data, ports };
23
25
  if (!started) {
@@ -53,6 +55,18 @@ export function createHouseBridgeTestHost(options = {}) {
53
55
  }
54
56
  ceremonies.push(target);
55
57
  }
58
+ function publish(next) {
59
+ status = next;
60
+ deliver({ house: 'status', status });
61
+ }
62
+ function consent(choice) {
63
+ if (status === null || !status.ga.measures)
64
+ return;
65
+ if (choice !== 'accept' && choice !== 'decline')
66
+ return;
67
+ consents.push(choice);
68
+ publish({ ...status, ga: { measures: true, choice } });
69
+ }
56
70
  function receive(message) {
57
71
  const data = message;
58
72
  if (data === null || typeof data !== 'object')
@@ -63,6 +77,9 @@ export function createHouseBridgeTestHost(options = {}) {
63
77
  else if (data.house === 'ceremony') {
64
78
  ceremony(data.path);
65
79
  }
80
+ else if (data.house === 'ga/consent') {
81
+ consent(data.choice);
82
+ }
66
83
  else if (data.house === 'attachment-read') {
67
84
  const asked = data;
68
85
  const image = options.images?.find((entry) => entry.room === asked.room && entry.attachment === asked.attachment);
@@ -98,22 +115,21 @@ export function createHouseBridgeTestHost(options = {}) {
98
115
  });
99
116
  },
100
117
  };
101
- if (options.status !== undefined)
102
- deliver({ house: 'status', status: options.status });
118
+ if (status !== null)
119
+ deliver({ house: 'status', status });
103
120
  deliver({ house: 'address', address, navigated: false });
104
121
  return {
105
122
  port: Promise.resolve(port),
106
123
  navigations,
107
124
  ceremonies,
125
+ consents,
108
126
  address() {
109
127
  return address;
110
128
  },
111
129
  open(asked) {
112
130
  arrive(appAddressOf('/', asked), false);
113
131
  },
114
- publish(status) {
115
- deliver({ house: 'status', status });
116
- },
132
+ publish,
117
133
  attachRuntime(runtime) {
118
134
  deliver({ house: 'runtime' }, [runtime]);
119
135
  },
@@ -16,6 +16,11 @@ export interface HouseCredentialState {
16
16
  readonly state: 'none' | 'held' | 'rejected';
17
17
  readonly expires_at: number | null;
18
18
  }
19
+ export type HouseGaChoice = 'accept' | 'decline';
20
+ export interface HouseGa {
21
+ readonly measures: boolean;
22
+ readonly choice: HouseGaChoice | null;
23
+ }
19
24
  export interface HouseStatus {
20
25
  readonly app: string;
21
26
  readonly runtime: string;
@@ -26,6 +31,7 @@ export interface HouseStatus {
26
31
  } | null;
27
32
  readonly loss: string | null;
28
33
  readonly release: unknown;
34
+ readonly ga: HouseGa;
29
35
  }
30
36
  export type HouseNavigationMode = 'push' | 'replace';
31
37
  export declare const HOUSE_ADDRESS_MAX_CHARS = 1000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/app-bridge",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "Renderer-side client of the agents.house App bridge: navigation, ceremonies, status and Site image reads, with an in-page test host.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",