@shamar/wire 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/README.md +210 -0
- package/client/wire.js +722 -0
- package/dist/component.d.ts +21 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +32 -0
- package/dist/component.js.map +1 -0
- package/dist/html.d.ts +3 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +11 -0
- package/dist/html.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +43 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +129 -0
- package/dist/kernel.js.map +1 -0
- package/dist/snapshot.d.ts +12 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +33 -0
- package/dist/snapshot.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public state for a Wire component. Only keys present on `data` are
|
|
3
|
+
* accepted from the browser.
|
|
4
|
+
*/
|
|
5
|
+
export interface WireEffects {
|
|
6
|
+
/** Full URL or path. The browser navigates after this response. */
|
|
7
|
+
redirect?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface WireComponent {
|
|
10
|
+
data: Record<string, unknown>;
|
|
11
|
+
/** Response-only effects. Not stored in the signed snapshot. */
|
|
12
|
+
effects?: WireEffects;
|
|
13
|
+
updating?(key: string, value: unknown): Promise<void> | void;
|
|
14
|
+
/** Called after a public property changes. */
|
|
15
|
+
updated?(key: string): Promise<void> | void;
|
|
16
|
+
/** Called for `wire:click` style actions. */
|
|
17
|
+
call?(method: string, params: unknown[]): Promise<void> | void;
|
|
18
|
+
}
|
|
19
|
+
export declare function applyUpdates(component: WireComponent, updates: Record<string, unknown> | undefined): string[];
|
|
20
|
+
export declare function invokeMethod(component: WireComponent, method: string, params?: unknown[]): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,gEAAgE;IAChE,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC7D,8CAA8C;IAC9C,OAAO,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5C,6CAA6C;IAC7C,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAChE;AAYD,wBAAgB,YAAY,CAAC,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,EAAE,CAS7G;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const BLOCKED = new Set([
|
|
2
|
+
'data',
|
|
3
|
+
'updated',
|
|
4
|
+
'call',
|
|
5
|
+
'constructor',
|
|
6
|
+
'hydrate',
|
|
7
|
+
'dehydrate',
|
|
8
|
+
'render',
|
|
9
|
+
]);
|
|
10
|
+
export function applyUpdates(component, updates) {
|
|
11
|
+
if (!updates)
|
|
12
|
+
return [];
|
|
13
|
+
const changed = [];
|
|
14
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
15
|
+
if (!(key in component.data) || BLOCKED.has(key))
|
|
16
|
+
continue;
|
|
17
|
+
component.data[key] = value;
|
|
18
|
+
changed.push(key);
|
|
19
|
+
}
|
|
20
|
+
return changed;
|
|
21
|
+
}
|
|
22
|
+
export async function invokeMethod(component, method, params = []) {
|
|
23
|
+
if (!method || BLOCKED.has(method) || method.startsWith('_')) {
|
|
24
|
+
throw new Error(`Unknown wire method: ${method}`);
|
|
25
|
+
}
|
|
26
|
+
const target = component;
|
|
27
|
+
const fn = target[method];
|
|
28
|
+
if (typeof fn !== 'function')
|
|
29
|
+
throw new Error(`Unknown wire method: ${method}`);
|
|
30
|
+
await fn.apply(component, params);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAoBA,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC;IACtB,MAAM;IACN,SAAS;IACT,MAAM;IACN,aAAa;IACb,SAAS;IACT,WAAW;IACX,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,UAAU,YAAY,CAAC,SAAwB,EAAE,OAA4C;IACjG,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC3D,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAwB,EACxB,MAAc,EACd,SAAoB,EAAE;IAEtB,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,MAAM,GAAG,SAA+C,CAAC;IAC/D,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,OAAO,EAAE,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IAChF,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC"}
|
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
package/dist/html.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function escapeHtml(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replace(/&/g, '&')
|
|
4
|
+
.replace(/</g, '<')
|
|
5
|
+
.replace(/>/g, '>')
|
|
6
|
+
.replace(/"/g, '"');
|
|
7
|
+
}
|
|
8
|
+
export function escapeAttr(value) {
|
|
9
|
+
return escapeHtml(value).replace(/'/g, ''');
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=html.js.map
|
package/dist/html.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Absolute path to the browser runtime (`wire.js`). Serve this file yourself, or let `registerWire` in `@shamar/adonis` serve it. */
|
|
2
|
+
export declare function wireClientPath(): string;
|
|
3
|
+
export { escapeAttr, escapeHtml } from './html.js';
|
|
4
|
+
export { applyUpdates, invokeMethod, type WireComponent, type WireEffects } from './component.js';
|
|
5
|
+
export { newComponentId, signSnapshot, verifySnapshot, type WireSnapshot } from './snapshot.js';
|
|
6
|
+
export { WireKernel, type WireDefinition, type WireEnvelope, type WireRequest } from './kernel.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,sIAAsI;AACtI,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
/** Absolute path to the browser runtime (`wire.js`). Serve this file yourself, or let `registerWire` in `@shamar/adonis` serve it. */
|
|
4
|
+
export function wireClientPath() {
|
|
5
|
+
return join(dirname(fileURLToPath(import.meta.url)), '../client/wire.js');
|
|
6
|
+
}
|
|
7
|
+
export { escapeAttr, escapeHtml } from './html.js';
|
|
8
|
+
export { applyUpdates, invokeMethod } from './component.js';
|
|
9
|
+
export { newComponentId, signSnapshot, verifySnapshot } from './snapshot.js';
|
|
10
|
+
export { WireKernel } from './kernel.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,sIAAsI;AACtI,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;AAC5E,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAwC,MAAM,gBAAgB,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAqB,MAAM,eAAe,CAAC;AAChG,OAAO,EAAE,UAAU,EAA4D,MAAM,aAAa,CAAC"}
|
package/dist/kernel.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type WireComponent } from './component.js';
|
|
2
|
+
import { type WireSnapshot } from './snapshot.js';
|
|
3
|
+
export interface WireDefinition {
|
|
4
|
+
create(): WireComponent;
|
|
5
|
+
/**
|
|
6
|
+
* Reload server-owned state after the snapshot is restored and before
|
|
7
|
+
* the request's updates and calls run. Use this for lists that live in a
|
|
8
|
+
* store, and keep client flags (such as `open`) yourself.
|
|
9
|
+
*/
|
|
10
|
+
refresh?(component: WireComponent): Promise<void> | void;
|
|
11
|
+
/** `slots` holds already-rendered child islands, keyed by slot name. */
|
|
12
|
+
render(component: WireComponent, slots?: Record<string, string>): string;
|
|
13
|
+
}
|
|
14
|
+
export interface WireRequest {
|
|
15
|
+
snapshot: WireSnapshot;
|
|
16
|
+
updates?: Record<string, unknown>;
|
|
17
|
+
calls?: Array<{
|
|
18
|
+
method: string;
|
|
19
|
+
params?: unknown[];
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export interface WireEnvelope {
|
|
23
|
+
snapshot: WireSnapshot;
|
|
24
|
+
html: string;
|
|
25
|
+
effects?: WireComponent['effects'];
|
|
26
|
+
}
|
|
27
|
+
export declare class WireKernel {
|
|
28
|
+
private readonly secret;
|
|
29
|
+
private readonly components;
|
|
30
|
+
constructor(secret: string, components: Record<string, WireDefinition>);
|
|
31
|
+
/**
|
|
32
|
+
* `slots` maps a slot name to a child component name. Children are separate
|
|
33
|
+
* islands: they keep their own snapshot and the browser does not morph them
|
|
34
|
+
* when the parent re-renders.
|
|
35
|
+
*/
|
|
36
|
+
mount(name: string, endpoint: string, slots?: Record<string, string>, state?: Record<string, unknown>): WireEnvelope;
|
|
37
|
+
update(request: WireRequest, endpoint: string): Promise<WireEnvelope>;
|
|
38
|
+
private mountChildren;
|
|
39
|
+
private renderChildren;
|
|
40
|
+
private require;
|
|
41
|
+
private island;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=kernel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAgD,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEhG,MAAM,WAAW,cAAc;IAC7B,MAAM,IAAI,aAAa,CAAC;IACxB;;;;OAIG;IACH,OAAO,CAAC,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACzD,wEAAwE;IACxE,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;CAC1E;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAC;CACvD;AAgBD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACpC;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAG7D;;;;OAIG;IACH,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,YAAY;IAqBT,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAwC3E,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,MAAM;CAIf"}
|
package/dist/kernel.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { escapeAttr } from './html.js';
|
|
2
|
+
import { invokeMethod } from './component.js';
|
|
3
|
+
import { newComponentId, signSnapshot, verifySnapshot } from './snapshot.js';
|
|
4
|
+
/**
|
|
5
|
+
* HTML attributes arrive as strings. When the class field is a number or
|
|
6
|
+
* boolean, `<wire:counter count="4" />` should still seed that field.
|
|
7
|
+
*/
|
|
8
|
+
function seedValue(current, incoming) {
|
|
9
|
+
if (typeof current === 'number' && typeof incoming === 'string' && /^-?\d+(\.\d+)?$/.test(incoming)) {
|
|
10
|
+
return Number(incoming);
|
|
11
|
+
}
|
|
12
|
+
if (typeof current === 'boolean' && typeof incoming === 'string') {
|
|
13
|
+
return incoming === 'true' || incoming === '1';
|
|
14
|
+
}
|
|
15
|
+
return incoming;
|
|
16
|
+
}
|
|
17
|
+
export class WireKernel {
|
|
18
|
+
secret;
|
|
19
|
+
components;
|
|
20
|
+
constructor(secret, components) {
|
|
21
|
+
this.secret = secret;
|
|
22
|
+
this.components = components;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `slots` maps a slot name to a child component name. Children are separate
|
|
26
|
+
* islands: they keep their own snapshot and the browser does not morph them
|
|
27
|
+
* when the parent re-renders.
|
|
28
|
+
*/
|
|
29
|
+
mount(name, endpoint, slots, state) {
|
|
30
|
+
const definition = this.require(name);
|
|
31
|
+
const component = definition.create();
|
|
32
|
+
if (state) {
|
|
33
|
+
for (const [key, value] of Object.entries(state)) {
|
|
34
|
+
if (key in component.data)
|
|
35
|
+
component.data[key] = seedValue(component.data[key], value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const nested = this.mountChildren(slots, endpoint);
|
|
39
|
+
const snapshot = signSnapshot(this.secret, {
|
|
40
|
+
id: newComponentId(),
|
|
41
|
+
name,
|
|
42
|
+
data: { ...component.data },
|
|
43
|
+
children: nested.snapshots,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
snapshot,
|
|
47
|
+
html: this.island(endpoint, snapshot, definition.render(component, nested.html)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async update(request, endpoint) {
|
|
51
|
+
const incoming = request?.snapshot;
|
|
52
|
+
if (!incoming || !verifySnapshot(this.secret, incoming)) {
|
|
53
|
+
throw new Error('Invalid wire snapshot');
|
|
54
|
+
}
|
|
55
|
+
const definition = this.require(incoming.name);
|
|
56
|
+
const component = definition.create();
|
|
57
|
+
for (const key of Object.keys(component.data)) {
|
|
58
|
+
if (key in (incoming.data ?? {}))
|
|
59
|
+
component.data[key] = incoming.data[key];
|
|
60
|
+
}
|
|
61
|
+
await definition.refresh?.(component);
|
|
62
|
+
for (const [key, value] of Object.entries(request.updates ?? {})) {
|
|
63
|
+
if (!(key in component.data))
|
|
64
|
+
continue;
|
|
65
|
+
await component.updating?.(key, value);
|
|
66
|
+
component.data[key] = value;
|
|
67
|
+
await component.updated?.(key);
|
|
68
|
+
const specific = `updated${key.charAt(0).toUpperCase()}${key.slice(1)}`;
|
|
69
|
+
const hook = component[specific];
|
|
70
|
+
if (typeof hook === 'function')
|
|
71
|
+
await hook.call(component);
|
|
72
|
+
}
|
|
73
|
+
for (const call of request.calls ?? []) {
|
|
74
|
+
await invokeMethod(component, call.method, call.params ?? []);
|
|
75
|
+
}
|
|
76
|
+
const nested = this.renderChildren(incoming.children, endpoint);
|
|
77
|
+
const snapshot = signSnapshot(this.secret, {
|
|
78
|
+
id: incoming.id,
|
|
79
|
+
name: incoming.name,
|
|
80
|
+
data: { ...component.data },
|
|
81
|
+
children: incoming.children,
|
|
82
|
+
});
|
|
83
|
+
const effects = component.effects?.redirect ? { redirect: component.effects.redirect } : undefined;
|
|
84
|
+
return {
|
|
85
|
+
snapshot,
|
|
86
|
+
html: this.island(endpoint, snapshot, definition.render(component, nested)),
|
|
87
|
+
effects,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
mountChildren(slots, endpoint) {
|
|
91
|
+
if (!slots || Object.keys(slots).length === 0)
|
|
92
|
+
return { html: {} };
|
|
93
|
+
const snapshots = {};
|
|
94
|
+
const html = {};
|
|
95
|
+
for (const [slot, childName] of Object.entries(slots)) {
|
|
96
|
+
const child = this.mount(childName, endpoint);
|
|
97
|
+
snapshots[slot] = child.snapshot;
|
|
98
|
+
html[slot] = child.html;
|
|
99
|
+
}
|
|
100
|
+
return { snapshots, html };
|
|
101
|
+
}
|
|
102
|
+
renderChildren(children, endpoint) {
|
|
103
|
+
const html = {};
|
|
104
|
+
for (const [slot, child] of Object.entries(children ?? {})) {
|
|
105
|
+
if (!verifySnapshot(this.secret, child)) {
|
|
106
|
+
throw new Error('Invalid wire snapshot');
|
|
107
|
+
}
|
|
108
|
+
const definition = this.require(child.name);
|
|
109
|
+
const component = definition.create();
|
|
110
|
+
for (const key of Object.keys(component.data)) {
|
|
111
|
+
if (key in (child.data ?? {}))
|
|
112
|
+
component.data[key] = child.data[key];
|
|
113
|
+
}
|
|
114
|
+
html[slot] = this.island(endpoint, child, definition.render(component));
|
|
115
|
+
}
|
|
116
|
+
return html;
|
|
117
|
+
}
|
|
118
|
+
require(name) {
|
|
119
|
+
const definition = this.components[name];
|
|
120
|
+
if (!definition)
|
|
121
|
+
throw new Error(`Unknown wire component: ${name}`);
|
|
122
|
+
return definition;
|
|
123
|
+
}
|
|
124
|
+
island(endpoint, snapshot, inner) {
|
|
125
|
+
const json = escapeAttr(JSON.stringify(snapshot));
|
|
126
|
+
return `<div wire:id="${escapeAttr(snapshot.id)}" wire:snapshot="${json}" wire:endpoint="${escapeAttr(endpoint)}">${inner}</div>`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=kernel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kernel.js","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface WireSnapshot {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
data: Record<string, unknown>;
|
|
5
|
+
checksum: string;
|
|
6
|
+
/** Stable child islands, keyed by slot name. Each child is signed on its own. */
|
|
7
|
+
children?: Record<string, WireSnapshot>;
|
|
8
|
+
}
|
|
9
|
+
export declare function newComponentId(): string;
|
|
10
|
+
export declare function signSnapshot(secret: string, snapshot: Omit<WireSnapshot, 'checksum'>): WireSnapshot;
|
|
11
|
+
export declare function verifySnapshot(secret: string, snapshot: WireSnapshot): boolean;
|
|
12
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAUD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,GACvC,YAAY,CAOd;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAW9E"}
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
export function newComponentId() {
|
|
3
|
+
return randomBytes(8).toString('hex');
|
|
4
|
+
}
|
|
5
|
+
function canonical(value) {
|
|
6
|
+
if (value === null || typeof value !== 'object')
|
|
7
|
+
return JSON.stringify(value);
|
|
8
|
+
if (Array.isArray(value))
|
|
9
|
+
return `[${value.map((item) => canonical(item)).join(',')}]`;
|
|
10
|
+
const record = value;
|
|
11
|
+
const keys = Object.keys(record).sort();
|
|
12
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonical(record[key])}`).join(',')}}`;
|
|
13
|
+
}
|
|
14
|
+
export function signSnapshot(secret, snapshot) {
|
|
15
|
+
const checksum = createHmac('sha256', secret)
|
|
16
|
+
.update(`${snapshot.id}:${snapshot.name}:${canonical(snapshot.data)}:${canonical(snapshot.children ?? null)}`)
|
|
17
|
+
.digest('hex');
|
|
18
|
+
return { ...snapshot, checksum };
|
|
19
|
+
}
|
|
20
|
+
export function verifySnapshot(secret, snapshot) {
|
|
21
|
+
const expected = signSnapshot(secret, {
|
|
22
|
+
id: snapshot.id,
|
|
23
|
+
name: snapshot.name,
|
|
24
|
+
data: snapshot.data,
|
|
25
|
+
children: snapshot.children,
|
|
26
|
+
});
|
|
27
|
+
const left = Buffer.from(expected.checksum);
|
|
28
|
+
const right = Buffer.from(String(snapshot.checksum ?? ''));
|
|
29
|
+
if (left.length !== right.length)
|
|
30
|
+
return false;
|
|
31
|
+
return timingSafeEqual(left, right);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAWvE,MAAM,UAAU,cAAc;IAC5B,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACvF,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,QAAwC;IAExC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;SAC1C,MAAM,CACL,GAAG,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CACtG;SACA,MAAM,CAAC,KAAK,CAAC,CAAC;IACjB,OAAO,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,QAAsB;IACnE,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE;QACpC,EAAE,EAAE,QAAQ,CAAC,EAAE;QACf,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3D,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/C,OAAO,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shamar/wire",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Server-driven reactive components. Usable on its own or inside a Shamar admin panel.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/coolsam726/shamar.git",
|
|
10
|
+
"directory": "packages/wire"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./client/wire.js": "./client/wire.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"client"
|
|
24
|
+
],
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.0.0",
|
|
27
|
+
"tsx": "^4.20.0",
|
|
28
|
+
"typescript": "^5.7.0"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"test": "tsx --test test/**/*.test.ts"
|
|
36
|
+
}
|
|
37
|
+
}
|