@effect.healthcare/messenger 0.4.2 → 0.6.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/README.md +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/messenger.d.ts +30 -13
- package/dist/messenger.js +66 -38
- package/dist/messenger.meta.d.ts +11 -5
- package/dist/messenger.meta.js +8 -2
- package/dist/messenger.validation.d.ts +3 -3
- package/dist/messenger.validation.js +4 -7
- package/package.json +7 -2
- package/.claude/CLAUDE.md +0 -88
- package/.claude/context/composition.md +0 -921
- package/.claude/context/data.md +0 -1036
- package/.claude/context/event.md +0 -686
- package/.claude/context/gesture.md +0 -614
- package/.claude/context/operation.md +0 -596
- package/.claude/context/state.md +0 -864
- package/.claude/context/testing.md +0 -876
- package/.claude/context/validation.md +0 -350
- package/.claude/context/workflow.md +0 -608
- package/.claude/settings.json +0 -7
- package/.mcp.json +0 -12
- package/dist/.gitkeep +0 -1
- package/src/index.ts +0 -16
- package/src/messenger.meta.ts +0 -132
- package/src/messenger.ts +0 -386
- package/src/messenger.validation.ts +0 -25
- package/web-dev-server.config.mjs +0 -8
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ It renders nothing — no template, no styles. You configure it, bind an event,
|
|
|
8
8
|
|
|
9
9
|
> This foundation is **receive‑only**; outbound collaboration messaging is a future capability.
|
|
10
10
|
|
|
11
|
+
The messenger is built on `@scalable.software/component` `^0.5.0`. The API, the events and their `detail` are unchanged. A host that serves the base component through an import map must serve `^0.5.0`.
|
|
12
|
+
|
|
11
13
|
## Overview
|
|
12
14
|
|
|
13
15
|
```js
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { Tag, Session, Protocol, Message, Attributes, State, Connection, Operation, Event, Gesture, } from "./messenger.meta.js";
|
|
1
|
+
export { Tag, Session, Endpoint, Protocol, Message, Attributes, State, Connection, Operation, Event, Gesture, } from "./messenger.meta.js";
|
|
2
|
+
export { type Path } from "./messenger.meta.js";
|
|
2
3
|
export { Validate } from "./messenger.validation.js";
|
|
3
4
|
export { Messenger } from "./messenger.js";
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Tag, Session, Protocol, Message, Attributes, State, Connection, Operation, Event, Gesture, } from "./messenger.meta.js";
|
|
1
|
+
export { Tag, Session, Endpoint, Protocol, Message, Attributes, State, Connection, Operation, Event, Gesture, } from "./messenger.meta.js";
|
|
2
2
|
export { Validate } from "./messenger.validation.js";
|
|
3
3
|
export { Messenger } from "./messenger.js";
|
package/dist/messenger.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Component } from "@scalable.software/component";
|
|
5
5
|
import { type Configuration } from "@scalable.software/component";
|
|
6
6
|
import { Attributes, Connection } from "./messenger.meta.js";
|
|
7
|
-
import { type
|
|
7
|
+
import { type Path } from "./messenger.meta.js";
|
|
8
8
|
/**
|
|
9
9
|
* Configuration for the headless component — no template or stylesheet.
|
|
10
10
|
* @category Configuration
|
|
@@ -31,11 +31,11 @@ export declare class Messenger extends Component {
|
|
|
31
31
|
*/
|
|
32
32
|
private _connection;
|
|
33
33
|
/**
|
|
34
|
-
* The negotiate
|
|
34
|
+
* The negotiate request path. When absent the messenger stays disconnected.
|
|
35
35
|
* @category State
|
|
36
36
|
* @hidden
|
|
37
37
|
*/
|
|
38
|
-
private
|
|
38
|
+
private _path;
|
|
39
39
|
/**
|
|
40
40
|
* The live Web PubSub socket. Held so it can be closed and replaced.
|
|
41
41
|
* @category State
|
|
@@ -47,7 +47,7 @@ export declare class Messenger extends Component {
|
|
|
47
47
|
* @category Event
|
|
48
48
|
* @hidden
|
|
49
49
|
*/
|
|
50
|
-
private
|
|
50
|
+
private _onpathchange;
|
|
51
51
|
private _onconnecting;
|
|
52
52
|
private _onconnected;
|
|
53
53
|
private _ondisconnected;
|
|
@@ -70,11 +70,11 @@ export declare class Messenger extends Component {
|
|
|
70
70
|
get connection(): Connection;
|
|
71
71
|
protected set connection(connection: Connection);
|
|
72
72
|
/**
|
|
73
|
-
* The negotiate
|
|
73
|
+
* The negotiate request path. When absent the messenger stays disconnected.
|
|
74
74
|
* @category State
|
|
75
75
|
*/
|
|
76
|
-
get
|
|
77
|
-
set
|
|
76
|
+
get path(): Path | null;
|
|
77
|
+
set path(path: Path | null);
|
|
78
78
|
/**
|
|
79
79
|
* The session token, read fresh from local storage on every access — never
|
|
80
80
|
* cached — because the License component renews and rewrites the key.
|
|
@@ -83,9 +83,18 @@ export declare class Messenger extends Component {
|
|
|
83
83
|
*/
|
|
84
84
|
private get _token();
|
|
85
85
|
/**
|
|
86
|
-
*
|
|
86
|
+
* The endpoint host base URL, read fresh from local storage on every access —
|
|
87
|
+
* never cached — mirroring the session token. Composed with the `path` state
|
|
88
|
+
* to form the negotiate URL. When absent the messenger stays disconnected.
|
|
89
|
+
* @category State
|
|
90
|
+
* @hidden
|
|
91
|
+
*/
|
|
92
|
+
private get _host();
|
|
93
|
+
/**
|
|
94
|
+
* Negotiates a client access URL from the configured path, authenticating
|
|
87
95
|
* with the session token, then opens the live connection. No-op when the
|
|
88
|
-
*
|
|
96
|
+
* path or token is unavailable, or when already connecting or connected.
|
|
97
|
+
* When negotiate fails the connection returns to `disconnected`.
|
|
89
98
|
* @category Operation
|
|
90
99
|
*/
|
|
91
100
|
connect: () => Promise<void>;
|
|
@@ -103,11 +112,11 @@ export declare class Messenger extends Component {
|
|
|
103
112
|
*/
|
|
104
113
|
send: (message: object) => boolean;
|
|
105
114
|
/**
|
|
106
|
-
* Subscribes to the `
|
|
115
|
+
* Subscribes to the `onpathchange` event. Assigning replaces the previous
|
|
107
116
|
* handler; assigning `null` removes it.
|
|
108
117
|
* @category Event
|
|
109
118
|
*/
|
|
110
|
-
set
|
|
119
|
+
set onpathchange(handler: EventListener | null);
|
|
111
120
|
/**
|
|
112
121
|
* Subscribes to the `onconnecting` event. Assigning replaces the previous
|
|
113
122
|
* handler; assigning `null` removes it.
|
|
@@ -139,7 +148,7 @@ export declare class Messenger extends Component {
|
|
|
139
148
|
*/
|
|
140
149
|
protected _attributeHandlers: {
|
|
141
150
|
connection: (value: Connection) => false | void;
|
|
142
|
-
|
|
151
|
+
path: (value: Path) => string;
|
|
143
152
|
};
|
|
144
153
|
/**
|
|
145
154
|
* Cache element references to improve performance
|
|
@@ -166,7 +175,15 @@ export declare class Messenger extends Component {
|
|
|
166
175
|
*/
|
|
167
176
|
protected _removeEventListeners: () => void;
|
|
168
177
|
/**
|
|
169
|
-
*
|
|
178
|
+
* Called by the disconnectedCallback prototypical method, after
|
|
179
|
+
* `_removeEventListeners`. Closes the live connection so a removed element
|
|
180
|
+
* does not keep a socket open.
|
|
181
|
+
* @category Configuration
|
|
182
|
+
* @hidden
|
|
183
|
+
*/
|
|
184
|
+
protected _dispose: () => void;
|
|
185
|
+
/**
|
|
186
|
+
* Negotiates a short-lived Web PubSub client access URL from the path,
|
|
170
187
|
* authenticating the request with the session token.
|
|
171
188
|
* @category Operation
|
|
172
189
|
* @hidden
|
package/dist/messenger.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Base Component
|
|
5
5
|
import { Component } from "@scalable.software/component";
|
|
6
6
|
// Component Metadata
|
|
7
|
-
import { Tag, Attributes, Connection, Event, Gesture, Session, Protocol, Message, } from "./messenger.meta.js";
|
|
7
|
+
import { Tag, Attributes, Connection, Event, Gesture, Session, Endpoint, Protocol, Message, } from "./messenger.meta.js";
|
|
8
8
|
// Component Validation
|
|
9
9
|
import { Validate } from "./messenger.validation.js";
|
|
10
10
|
/**
|
|
@@ -39,11 +39,11 @@ export class Messenger extends Component {
|
|
|
39
39
|
*/
|
|
40
40
|
_connection = Connection.DISCONNECTED;
|
|
41
41
|
/**
|
|
42
|
-
* The negotiate
|
|
42
|
+
* The negotiate request path. When absent the messenger stays disconnected.
|
|
43
43
|
* @category State
|
|
44
44
|
* @hidden
|
|
45
45
|
*/
|
|
46
|
-
|
|
46
|
+
_path = null;
|
|
47
47
|
/**
|
|
48
48
|
* The live Web PubSub socket. Held so it can be closed and replaced.
|
|
49
49
|
* @category State
|
|
@@ -55,7 +55,7 @@ export class Messenger extends Component {
|
|
|
55
55
|
* @category Event
|
|
56
56
|
* @hidden
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
_onpathchange = null;
|
|
59
59
|
_onconnecting = null;
|
|
60
60
|
_onconnected = null;
|
|
61
61
|
_ondisconnected = null;
|
|
@@ -88,36 +88,36 @@ export class Messenger extends Component {
|
|
|
88
88
|
this._connection = connection;
|
|
89
89
|
this.setAttribute(Attributes.CONNECTION, connection);
|
|
90
90
|
// Dispatch
|
|
91
|
-
const
|
|
91
|
+
const detail = { connection };
|
|
92
92
|
connection === Connection.CONNECTING &&
|
|
93
|
-
this.
|
|
93
|
+
this._dispatch(Event.ON_CONNECTING, detail);
|
|
94
94
|
connection === Connection.CONNECTED &&
|
|
95
|
-
this.
|
|
95
|
+
this._dispatch(Event.ON_CONNECTED, detail);
|
|
96
96
|
connection === Connection.DISCONNECTED &&
|
|
97
|
-
this.
|
|
97
|
+
this._dispatch(Event.ON_DISCONNECTED, detail);
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
* The negotiate
|
|
100
|
+
* The negotiate request path. When absent the messenger stays disconnected.
|
|
101
101
|
* @category State
|
|
102
102
|
*/
|
|
103
|
-
get
|
|
104
|
-
return this.
|
|
103
|
+
get path() {
|
|
104
|
+
return this._path;
|
|
105
105
|
}
|
|
106
|
-
set
|
|
106
|
+
set path(path) {
|
|
107
107
|
// Normalize
|
|
108
|
-
|
|
108
|
+
path = path ?? null;
|
|
109
109
|
// Validate
|
|
110
|
-
|
|
110
|
+
path = path ? Validate.path(path) : null;
|
|
111
111
|
// Guard
|
|
112
|
-
if (this.
|
|
112
|
+
if (this._path === path)
|
|
113
113
|
return;
|
|
114
114
|
// Mutate
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
? this.removeAttribute(Attributes.
|
|
118
|
-
: this.setAttribute(Attributes.
|
|
115
|
+
this._path = path;
|
|
116
|
+
path === null
|
|
117
|
+
? this.removeAttribute(Attributes.PATH)
|
|
118
|
+
: this.setAttribute(Attributes.PATH, path);
|
|
119
119
|
// Dispatch
|
|
120
|
-
this.
|
|
120
|
+
this._dispatch(Event.ON_PATH_CHANGE, { path });
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
123
|
* The session token, read fresh from local storage on every access — never
|
|
@@ -129,22 +129,40 @@ export class Messenger extends Component {
|
|
|
129
129
|
return localStorage.getItem(Session);
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
132
|
-
*
|
|
132
|
+
* The endpoint host base URL, read fresh from local storage on every access —
|
|
133
|
+
* never cached — mirroring the session token. Composed with the `path` state
|
|
134
|
+
* to form the negotiate URL. When absent the messenger stays disconnected.
|
|
135
|
+
* @category State
|
|
136
|
+
* @hidden
|
|
137
|
+
*/
|
|
138
|
+
get _host() {
|
|
139
|
+
return localStorage.getItem(Endpoint);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Negotiates a client access URL from the configured path, authenticating
|
|
133
143
|
* with the session token, then opens the live connection. No-op when the
|
|
134
|
-
*
|
|
144
|
+
* path or token is unavailable, or when already connecting or connected.
|
|
145
|
+
* When negotiate fails the connection returns to `disconnected`.
|
|
135
146
|
* @category Operation
|
|
136
147
|
*/
|
|
137
148
|
connect = async () => {
|
|
138
149
|
const token = this._token;
|
|
139
150
|
// Gate + guard
|
|
140
|
-
if (!this.
|
|
151
|
+
if (!this._host || !this.path || !token)
|
|
141
152
|
return;
|
|
142
153
|
if (this._connection !== Connection.DISCONNECTED)
|
|
143
154
|
return;
|
|
144
|
-
// Establish
|
|
155
|
+
// Establish — on negotiate failure, recover to disconnected
|
|
145
156
|
this.connection = Connection.CONNECTING;
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
try {
|
|
158
|
+
const url = await this._negotiate(token);
|
|
159
|
+
if (this.connection !== Connection.CONNECTING)
|
|
160
|
+
return;
|
|
161
|
+
this._socket = this._open(url);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
this.connection = Connection.DISCONNECTED;
|
|
165
|
+
}
|
|
148
166
|
};
|
|
149
167
|
/**
|
|
150
168
|
* Closes the live connection.
|
|
@@ -176,16 +194,16 @@ export class Messenger extends Component {
|
|
|
176
194
|
return true;
|
|
177
195
|
};
|
|
178
196
|
/**
|
|
179
|
-
* Subscribes to the `
|
|
197
|
+
* Subscribes to the `onpathchange` event. Assigning replaces the previous
|
|
180
198
|
* handler; assigning `null` removes it.
|
|
181
199
|
* @category Event
|
|
182
200
|
*/
|
|
183
|
-
set
|
|
184
|
-
this.
|
|
185
|
-
this.removeEventListener(Event.
|
|
186
|
-
this.
|
|
187
|
-
this.
|
|
188
|
-
this.addEventListener(Event.
|
|
201
|
+
set onpathchange(handler) {
|
|
202
|
+
this._onpathchange &&
|
|
203
|
+
this.removeEventListener(Event.ON_PATH_CHANGE, this._onpathchange);
|
|
204
|
+
this._onpathchange = handler;
|
|
205
|
+
this._onpathchange &&
|
|
206
|
+
this.addEventListener(Event.ON_PATH_CHANGE, this._onpathchange);
|
|
189
207
|
}
|
|
190
208
|
/**
|
|
191
209
|
* Subscribes to the `onconnecting` event. Assigning replaces the previous
|
|
@@ -243,7 +261,7 @@ export class Messenger extends Component {
|
|
|
243
261
|
_attributeHandlers = {
|
|
244
262
|
[Attributes.CONNECTION]: (value) => value !== this._connection &&
|
|
245
263
|
this.setAttribute(Attributes.CONNECTION, this._connection),
|
|
246
|
-
[Attributes.
|
|
264
|
+
[Attributes.PATH]: (value) => (this.path = value),
|
|
247
265
|
};
|
|
248
266
|
/**
|
|
249
267
|
* Cache element references to improve performance
|
|
@@ -273,16 +291,26 @@ export class Messenger extends Component {
|
|
|
273
291
|
*/
|
|
274
292
|
_removeEventListeners = () => { };
|
|
275
293
|
/**
|
|
276
|
-
*
|
|
294
|
+
* Called by the disconnectedCallback prototypical method, after
|
|
295
|
+
* `_removeEventListeners`. Closes the live connection so a removed element
|
|
296
|
+
* does not keep a socket open.
|
|
297
|
+
* @category Configuration
|
|
298
|
+
* @hidden
|
|
299
|
+
*/
|
|
300
|
+
_dispose = () => this.disconnect();
|
|
301
|
+
/**
|
|
302
|
+
* Negotiates a short-lived Web PubSub client access URL from the path,
|
|
277
303
|
* authenticating the request with the session token.
|
|
278
304
|
* @category Operation
|
|
279
305
|
* @hidden
|
|
280
306
|
*/
|
|
281
307
|
_negotiate = async (token) => {
|
|
282
|
-
const response = await fetch(this.
|
|
308
|
+
const response = await fetch(`${this._host}${this.path}`, {
|
|
283
309
|
headers: { Authorization: `Bearer ${token}` },
|
|
284
310
|
});
|
|
285
311
|
const { url } = await response.json();
|
|
312
|
+
if (!url)
|
|
313
|
+
throw new Error("Invalid negotiate response: missing url");
|
|
286
314
|
return url;
|
|
287
315
|
};
|
|
288
316
|
/**
|
|
@@ -307,8 +335,8 @@ export class Messenger extends Component {
|
|
|
307
335
|
_handleMessage = (event) => {
|
|
308
336
|
const message = JSON.parse(event.data);
|
|
309
337
|
message.data?.type === Message.MASTER_DATA_CHANGED &&
|
|
310
|
-
this.
|
|
311
|
-
|
|
338
|
+
this._dispatch(Event.ON_MASTERDATACHANGE, {
|
|
339
|
+
datetime: new Date().toISOString(),
|
|
312
340
|
});
|
|
313
341
|
};
|
|
314
342
|
}
|
package/dist/messenger.meta.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ export declare const Tag: "app-messenger";
|
|
|
11
11
|
* @category Configuration
|
|
12
12
|
*/
|
|
13
13
|
export declare const Session: "app-license.session";
|
|
14
|
+
/**
|
|
15
|
+
* Local storage key under which the endpoint host base URL is cached by the
|
|
16
|
+
* Configurator component. Composed with the `path` state to form the negotiate URL.
|
|
17
|
+
* @category Configuration
|
|
18
|
+
*/
|
|
19
|
+
export declare const Endpoint: "data";
|
|
14
20
|
/**
|
|
15
21
|
* The Azure Web PubSub subprotocol required for structured group messaging.
|
|
16
22
|
* @category Configuration
|
|
@@ -36,7 +42,7 @@ export type Message = (typeof Message)[keyof typeof Message];
|
|
|
36
42
|
*/
|
|
37
43
|
export declare const Attributes: {
|
|
38
44
|
readonly CONNECTION: "connection";
|
|
39
|
-
readonly
|
|
45
|
+
readonly PATH: "path";
|
|
40
46
|
};
|
|
41
47
|
/**
|
|
42
48
|
* HTML Attributes available to set
|
|
@@ -50,7 +56,7 @@ export type Attributes = typeof Attributes;
|
|
|
50
56
|
*/
|
|
51
57
|
export declare const State: {
|
|
52
58
|
readonly CONNECTION: "connection";
|
|
53
|
-
readonly
|
|
59
|
+
readonly PATH: "path";
|
|
54
60
|
};
|
|
55
61
|
/**
|
|
56
62
|
* HTML Attributes available to set
|
|
@@ -73,10 +79,10 @@ export declare const Connection: {
|
|
|
73
79
|
*/
|
|
74
80
|
export type Connection = (typeof Connection)[keyof typeof Connection];
|
|
75
81
|
/**
|
|
76
|
-
* The negotiate
|
|
82
|
+
* The negotiate request path value domain
|
|
77
83
|
* @category Metadata
|
|
78
84
|
*/
|
|
79
|
-
export type
|
|
85
|
+
export type Path = string;
|
|
80
86
|
/**
|
|
81
87
|
* @category Metadata
|
|
82
88
|
* @enum
|
|
@@ -95,7 +101,7 @@ export type Operation = (typeof Operation)[keyof typeof Operation];
|
|
|
95
101
|
* @enum
|
|
96
102
|
*/
|
|
97
103
|
export declare const Event: {
|
|
98
|
-
readonly
|
|
104
|
+
readonly ON_PATH_CHANGE: "onpathchange";
|
|
99
105
|
readonly ON_CONNECTING: "onconnecting";
|
|
100
106
|
readonly ON_CONNECTED: "onconnected";
|
|
101
107
|
readonly ON_DISCONNECTED: "ondisconnected";
|
package/dist/messenger.meta.js
CHANGED
|
@@ -11,6 +11,12 @@ export const Tag = "app-messenger";
|
|
|
11
11
|
* @category Configuration
|
|
12
12
|
*/
|
|
13
13
|
export const Session = "app-license.session";
|
|
14
|
+
/**
|
|
15
|
+
* Local storage key under which the endpoint host base URL is cached by the
|
|
16
|
+
* Configurator component. Composed with the `path` state to form the negotiate URL.
|
|
17
|
+
* @category Configuration
|
|
18
|
+
*/
|
|
19
|
+
export const Endpoint = "data";
|
|
14
20
|
/**
|
|
15
21
|
* The Azure Web PubSub subprotocol required for structured group messaging.
|
|
16
22
|
* @category Configuration
|
|
@@ -31,7 +37,7 @@ export const Message = {
|
|
|
31
37
|
*/
|
|
32
38
|
export const Attributes = {
|
|
33
39
|
CONNECTION: "connection",
|
|
34
|
-
|
|
40
|
+
PATH: "path",
|
|
35
41
|
};
|
|
36
42
|
/**
|
|
37
43
|
* HTML Attributes available to set
|
|
@@ -65,7 +71,7 @@ export const Operation = {
|
|
|
65
71
|
* @enum
|
|
66
72
|
*/
|
|
67
73
|
export const Event = {
|
|
68
|
-
|
|
74
|
+
ON_PATH_CHANGE: "onpathchange",
|
|
69
75
|
ON_CONNECTING: "onconnecting",
|
|
70
76
|
ON_CONNECTED: "onconnected",
|
|
71
77
|
ON_DISCONNECTED: "ondisconnected",
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module Component
|
|
3
3
|
*/
|
|
4
|
-
import { type
|
|
4
|
+
import { type Path } from "./messenger.meta.js";
|
|
5
5
|
/**
|
|
6
6
|
* Runtime enforcement of declared value domains.
|
|
7
7
|
* @category Validation
|
|
8
8
|
*/
|
|
9
9
|
export declare class Validate {
|
|
10
10
|
/**
|
|
11
|
-
* Validates the negotiate
|
|
11
|
+
* Validates the negotiate request path value domain.
|
|
12
12
|
* @category Validation
|
|
13
13
|
*/
|
|
14
|
-
static
|
|
14
|
+
static path: (value: string) => Path;
|
|
15
15
|
}
|
|
@@ -7,15 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export class Validate {
|
|
9
9
|
/**
|
|
10
|
-
* Validates the negotiate
|
|
10
|
+
* Validates the negotiate request path value domain.
|
|
11
11
|
* @category Validation
|
|
12
12
|
*/
|
|
13
|
-
static
|
|
14
|
-
|
|
15
|
-
new
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
throw new Error(`Invalid endpoint value: ${value}`);
|
|
13
|
+
static path = (value) => {
|
|
14
|
+
if (!value.startsWith("/")) {
|
|
15
|
+
throw new Error(`Invalid path value: ${value}`);
|
|
19
16
|
}
|
|
20
17
|
return value;
|
|
21
18
|
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect.healthcare/messenger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Azure Pub Sub Messenger Web Component",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web component"
|
|
7
7
|
],
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"!dist/.gitkeep"
|
|
11
|
+
],
|
|
8
12
|
"exports": {
|
|
9
13
|
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
10
15
|
"import": "./dist/index.js"
|
|
11
16
|
}
|
|
12
17
|
},
|
|
@@ -50,6 +55,6 @@
|
|
|
50
55
|
"typescript": "^6.0.3"
|
|
51
56
|
},
|
|
52
57
|
"dependencies": {
|
|
53
|
-
"@scalable.software/component": "^0.
|
|
58
|
+
"@scalable.software/component": "^0.5.0"
|
|
54
59
|
}
|
|
55
60
|
}
|
package/.claude/CLAUDE.md
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# Web Component
|
|
2
|
-
|
|
3
|
-
A web component built on the `@scalable.software/component` framework. The policies in `.claude/context/` govern how component specifications are transformed into solid implementations.
|
|
4
|
-
|
|
5
|
-
## How This Project Works
|
|
6
|
-
|
|
7
|
-
Every component is developed through a specification-driven, test-first pipeline. The specification declares the public contract; the implementation honors it; the tests verify it; the commit history records the transformation.
|
|
8
|
-
|
|
9
|
-
When working on a component, translate the specification into an implementation that conforms to all applicable policies — without inventing structure, behavior, or vocabulary the specification does not declare.
|
|
10
|
-
|
|
11
|
-
## Translation Pipeline
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
<component>.specifications.json ← source of truth
|
|
15
|
-
│
|
|
16
|
-
├─→ <component>.meta.ts vocabulary + value-domain types
|
|
17
|
-
├─→ <component>.validation.ts Validate class — runtime enforcement
|
|
18
|
-
├─→ <component>.ts state, operations, events, gestures
|
|
19
|
-
├─→ <component>.template.html realized composition
|
|
20
|
-
└─→ <component>.style.css realized presentation
|
|
21
|
-
|
|
22
|
-
verified by:
|
|
23
|
-
test/unit/<component>.meta.test.ts — vocabulary tests
|
|
24
|
-
test/unit/<component>.validation.test.ts — validator tests
|
|
25
|
-
test/unit/<component>.test.ts — component behavioral tests
|
|
26
|
-
|
|
27
|
-
recorded by:
|
|
28
|
-
FAIL/PASS commit pairs, policy-ordered, one prefix per commit
|
|
29
|
-
DOCUMENTATION → FEATURE closes each feature branch
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Names predictably transform across layers. A state named `visibility` in the specification becomes `Attributes.VISIBILITY` + `State.VISIBILITY` in metadata, `Validate.visibility` in validation, `get visibility()`/`set visibility(...)` on the class, `state(State.VISIBILITY, ...)` in tests, and `STATE: ...` in commits.
|
|
33
|
-
|
|
34
|
-
## Implementation Order
|
|
35
|
-
|
|
36
|
-
Within any behavioral feature, work proceeds top-down through the policy hierarchy:
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
COMPOSITION → STATE → VALIDATION → OPERATION → EVENT → STYLE → GESTURE → DOCUMENTATION → FEATURE
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Each layer depends on the layers above. CSS-only features collapse to `STYLE → FEATURE`. See `@context/workflow.md` for the full sequence and per-layer commit format.
|
|
43
|
-
|
|
44
|
-
## Implementation Policies
|
|
45
|
-
|
|
46
|
-
Each policy file opens with a **How to Read This Policy** rubric that classifies its rules as one of three strengths:
|
|
47
|
-
|
|
48
|
-
- **Principles** — non-negotiable; derived from framework correctness or TDD discipline.
|
|
49
|
-
- **Templates** — invariant skeletons with explicit variation points based on declared classification (e.g., attribute-backed vs non-attribute, readonly vs writable, payload-bearing vs payload-free).
|
|
50
|
-
- **Conventions** — canonical defaults that keep implementations readable across components.
|
|
51
|
-
|
|
52
|
-
When a new component's shape conflicts with a rule, classify the rule before reconciling: a principle conflict is a defect; a template conflict means a variation point needs to be made explicit; a convention conflict is a documented deviation.
|
|
53
|
-
|
|
54
|
-
### Specification-driven (per component)
|
|
55
|
-
|
|
56
|
-
@context/composition.md — structural parts and cached references
|
|
57
|
-
@context/data.md — canonical component-owned content
|
|
58
|
-
@context/state.md — runtime mode, visibility, status, persistence
|
|
59
|
-
@context/operation.md — public imperative actions
|
|
60
|
-
@context/event.md — observable semantic outcomes
|
|
61
|
-
@context/gesture.md — interaction inputs
|
|
62
|
-
@context/validation.md — runtime enforcement of declared value domains
|
|
63
|
-
|
|
64
|
-
### Cross-cutting (every component)
|
|
65
|
-
|
|
66
|
-
@context/testing.md — test file structure, BDD vocabulary, coverage contracts
|
|
67
|
-
@context/workflow.md — branching, commit sequencing, TDD discipline
|
|
68
|
-
|
|
69
|
-
## Working Principles
|
|
70
|
-
|
|
71
|
-
- **Test first.** Every behavioral change starts with a failing test. The FAIL commit precedes the PASS commit. No exceptions.
|
|
72
|
-
- **One feature per branch.** Behavioral work is never committed directly to main.
|
|
73
|
-
- **One concern per commit.** Every commit message begins with a canonical prefix.
|
|
74
|
-
- **Trace names across layers.** A specification name reappears unchanged — modulo standard transformations — in metadata, validation, class API, tests, and commits.
|
|
75
|
-
- **Specification is authoritative.** Implementations do not invent structure, behavior, or vocabulary the specification does not declare. If a component's shape does not fit a policy rule, examine the policy's variation points — do not bypass them.
|
|
76
|
-
|
|
77
|
-
## Running the Demo in a Browser (chrome-devtools MCP)
|
|
78
|
-
|
|
79
|
-
For advanced testing or debugging, the live web component can be served and driven through the `chrome-devtools` MCP. This gives access to the rendered component, its shadow DOM, the console, and DOM manipulation.
|
|
80
|
-
|
|
81
|
-
1. **Start the dev server** — `npm run serve`. The `preserve` hook builds first (`src/` → `dist/`), and `web-dev-server.config.mjs` is auto-loaded (`rootDir: "."`, `port: 8000`, `watch: true`). Run it in the background. The server serves the repo root because the demo loads `../importmap` and `./dist` via the import map.
|
|
82
|
-
2. **Navigate the controlled browser** — `mcp__chrome-devtools__navigate_page` to `http://localhost:8000/demo/index.html`. Always use the explicit `/demo/index.html` path; the demo uses relative asset paths and will not resolve at `/`.
|
|
83
|
-
3. **Verify** — `mcp__chrome-devtools__evaluate_script` to inspect the component (e.g. `customElements.get("app-messenger")` is defined; the base framework exposes the shadow root as `el.root`); `mcp__chrome-devtools__list_console_messages` to check for errors.
|
|
84
|
-
4. **Clean up when done** — navigate the page to `about:blank` (the MCP keeps one page alive, so the last page cannot be closed), then stop the dev server process on port 8000.
|
|
85
|
-
|
|
86
|
-
Notes:
|
|
87
|
-
- MCP tool schemas must be loaded via `ToolSearch` (`select:<name>`) before first use each session.
|
|
88
|
-
- If a stale server already holds port 8000, the new one fails to bind — kill the straggler rather than letting it drift to another port.
|