@agentshouse/app-bridge 0.1.0-alpha.2 → 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 +13 -4
- package/{bridge.d.ts → client/src/bridge.d.ts} +2 -1
- package/{bridge.js → client/src/bridge.js} +3 -0
- package/{index.d.ts → client/src/index.d.ts} +1 -1
- package/{test-host.d.ts → client/src/test-host.d.ts} +2 -1
- package/{test-host.js → client/src/test-host.js} +24 -9
- package/{wire.d.ts → client/src/wire.d.ts} +6 -0
- package/package.json +9 -7
- package/src/apps/remainder.d.ts +2 -0
- package/src/apps/remainder.js +16 -0
- /package/{index.js → client/src/index.js} +0 -0
- /package/{wire.js → client/src/wire.js} +0 -0
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.
|
|
@@ -66,6 +70,11 @@ host.open('/rooms/atlas');
|
|
|
66
70
|
```
|
|
67
71
|
|
|
68
72
|
The host plays the shell and the worker: it delivers the address the App starts
|
|
69
|
-
at
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
at and each address `open` plays as Back or Forward through the shell's own
|
|
74
|
+
slice below the App's path, admitting nothing, so it delivers `//notes` exactly
|
|
75
|
+
as a browser at `/apps/<app>//notes` does. It records every push and replace
|
|
76
|
+
and answers each with the address the shell would then show, records an
|
|
77
|
+
admitted ceremony, refuses a push, replace or ceremony that is not a
|
|
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;
|
|
@@ -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;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appAddressOf } from '../../src/apps/remainder.js';
|
|
1
2
|
import { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
|
|
2
3
|
const HOST_ORIGIN = 'https://house.invalid';
|
|
3
4
|
function sameOriginPath(value) {
|
|
@@ -13,10 +14,12 @@ function sameOriginPath(value) {
|
|
|
13
14
|
export function createHouseBridgeTestHost(options = {}) {
|
|
14
15
|
const navigations = [];
|
|
15
16
|
const ceremonies = [];
|
|
17
|
+
const consents = [];
|
|
16
18
|
const listeners = new Set();
|
|
17
19
|
const queued = [];
|
|
18
20
|
let started = false;
|
|
19
|
-
let address =
|
|
21
|
+
let address = appAddressOf('/', options.address ?? '/');
|
|
22
|
+
let status = options.status ?? null;
|
|
20
23
|
function deliver(data, ports = []) {
|
|
21
24
|
const event = { data, ports };
|
|
22
25
|
if (!started) {
|
|
@@ -52,6 +55,18 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
52
55
|
}
|
|
53
56
|
ceremonies.push(target);
|
|
54
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
|
+
}
|
|
55
70
|
function receive(message) {
|
|
56
71
|
const data = message;
|
|
57
72
|
if (data === null || typeof data !== 'object')
|
|
@@ -62,6 +77,9 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
62
77
|
else if (data.house === 'ceremony') {
|
|
63
78
|
ceremony(data.path);
|
|
64
79
|
}
|
|
80
|
+
else if (data.house === 'ga/consent') {
|
|
81
|
+
consent(data.choice);
|
|
82
|
+
}
|
|
65
83
|
else if (data.house === 'attachment-read') {
|
|
66
84
|
const asked = data;
|
|
67
85
|
const image = options.images?.find((entry) => entry.room === asked.room && entry.attachment === asked.attachment);
|
|
@@ -97,24 +115,21 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
97
115
|
});
|
|
98
116
|
},
|
|
99
117
|
};
|
|
100
|
-
if (
|
|
101
|
-
deliver({ house: 'status', status
|
|
118
|
+
if (status !== null)
|
|
119
|
+
deliver({ house: 'status', status });
|
|
102
120
|
deliver({ house: 'address', address, navigated: false });
|
|
103
121
|
return {
|
|
104
122
|
port: Promise.resolve(port),
|
|
105
123
|
navigations,
|
|
106
124
|
ceremonies,
|
|
125
|
+
consents,
|
|
107
126
|
address() {
|
|
108
127
|
return address;
|
|
109
128
|
},
|
|
110
129
|
open(asked) {
|
|
111
|
-
|
|
112
|
-
if (target !== null)
|
|
113
|
-
arrive(target, false);
|
|
114
|
-
},
|
|
115
|
-
publish(status) {
|
|
116
|
-
deliver({ house: 'status', status });
|
|
130
|
+
arrive(appAddressOf('/', asked), false);
|
|
117
131
|
},
|
|
132
|
+
publish,
|
|
118
133
|
attachRuntime(runtime) {
|
|
119
134
|
deliver({ house: 'runtime' }, [runtime]);
|
|
120
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
|
+
"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",
|
|
@@ -10,17 +10,19 @@
|
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
-
"types": "./index.d.ts",
|
|
14
|
-
"default": "./index.js"
|
|
13
|
+
"types": "./client/src/index.d.ts",
|
|
14
|
+
"default": "./client/src/index.js"
|
|
15
15
|
},
|
|
16
16
|
"./test-host": {
|
|
17
|
-
"types": "./test-host.d.ts",
|
|
18
|
-
"default": "./test-host.js"
|
|
17
|
+
"types": "./client/src/test-host.d.ts",
|
|
18
|
+
"default": "./client/src/test-host.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"client/src/*.js",
|
|
23
|
+
"client/src/*.d.ts",
|
|
24
|
+
"src/apps/remainder.js",
|
|
25
|
+
"src/apps/remainder.d.ts",
|
|
24
26
|
"README.md"
|
|
25
27
|
]
|
|
26
28
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function split(address) {
|
|
2
|
+
const mark = address.search(/[?#]/u);
|
|
3
|
+
return mark < 0
|
|
4
|
+
? { path: address, rest: '' }
|
|
5
|
+
: { path: address.slice(0, mark), rest: address.slice(mark) };
|
|
6
|
+
}
|
|
7
|
+
export function appAddressOf(root, address) {
|
|
8
|
+
const { path, rest } = split(address);
|
|
9
|
+
const below = root === '/' ? path : path.slice(root.length);
|
|
10
|
+
return `${below.startsWith('/') ? below : `/${below}`}${rest}`;
|
|
11
|
+
}
|
|
12
|
+
export function shellAddressOf(root, appAddress) {
|
|
13
|
+
const { path, rest } = split(appAddress);
|
|
14
|
+
const below = path === '/' ? '' : path;
|
|
15
|
+
return `${root === '/' ? '' : root}${below}${rest}` || '/';
|
|
16
|
+
}
|
|
File without changes
|
|
File without changes
|