@1kbgz/transports 0.3.0 → 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/dist/cdn/index.js +247 -13
- package/dist/cdn/index.js.map +3 -3
- package/dist/esm/index.js +247 -13
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/src/ts/bridge.d.ts +26 -0
- package/dist/esm/src/ts/client.d.ts +99 -0
- package/dist/esm/src/ts/codecs.d.ts +17 -0
- package/dist/esm/src/ts/index.d.ts +31 -0
- package/dist/pkg/transports.d.ts +19 -7
- package/dist/pkg/transports.js +45 -2
- package/dist/pkg/transports_bg.wasm +0 -0
- package/dist/pkg/transports_bg.wasm.d.ts +9 -7
- package/package.json +15 -11
- package/dist/html/index.html +0 -13
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Value } from "./bridge";
|
|
2
|
+
export type PathSeg = {
|
|
3
|
+
Key: string;
|
|
4
|
+
} | {
|
|
5
|
+
Index: number;
|
|
6
|
+
};
|
|
7
|
+
export type PatchOp = {
|
|
8
|
+
Set: {
|
|
9
|
+
path: PathSeg[];
|
|
10
|
+
value: Value;
|
|
11
|
+
};
|
|
12
|
+
} | {
|
|
13
|
+
Remove: {
|
|
14
|
+
path: PathSeg[];
|
|
15
|
+
};
|
|
16
|
+
} | {
|
|
17
|
+
Insert: {
|
|
18
|
+
path: PathSeg[];
|
|
19
|
+
index: number;
|
|
20
|
+
value: Value;
|
|
21
|
+
};
|
|
22
|
+
} | {
|
|
23
|
+
RemoveAt: {
|
|
24
|
+
path: PathSeg[];
|
|
25
|
+
index: number;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type ModelPatch = {
|
|
29
|
+
rev: number;
|
|
30
|
+
ops: PatchOp[];
|
|
31
|
+
};
|
|
32
|
+
type PatchMsg = {
|
|
33
|
+
t: "patch";
|
|
34
|
+
id: number;
|
|
35
|
+
patch: ModelPatch;
|
|
36
|
+
};
|
|
37
|
+
/** Frame metadata returned after the mirror accepts a snapshot or patch. */
|
|
38
|
+
export type ReceiveChange = {
|
|
39
|
+
t: "snapshot";
|
|
40
|
+
id: number;
|
|
41
|
+
rev: number;
|
|
42
|
+
} | PatchMsg;
|
|
43
|
+
/** Mirrors a remote transports `Session` from connection messages.
|
|
44
|
+
*
|
|
45
|
+
* Inbound frames are decoded by type — text frames are JSON, binary frames are MessagePack — so a
|
|
46
|
+
* client transparently mirrors a server regardless of the negotiated codec. Binary built-in codecs
|
|
47
|
+
* and edit generation require the wasm core to be initialized.
|
|
48
|
+
*/
|
|
49
|
+
export declare class Client {
|
|
50
|
+
private codec;
|
|
51
|
+
private values;
|
|
52
|
+
private revs;
|
|
53
|
+
constructor(codec?: string);
|
|
54
|
+
/** Apply an inbound snapshot or patch frame to the mirror.
|
|
55
|
+
*
|
|
56
|
+
* Decodes by the client's codec: a registered custom codec, else built-in JSON (text) / msgpack
|
|
57
|
+
* (binary). Returns the accepted change so reactive adapters can update only its paths; returns
|
|
58
|
+
* `undefined` for a patch whose revision was already applied. The returned change and values from
|
|
59
|
+
* `value()` share immutable branches with the mirror; consumers must not mutate them. Invalid frames
|
|
60
|
+
* throw without changing the mirror or its accepted revision.
|
|
61
|
+
*/
|
|
62
|
+
recv(data: string | Uint8Array): ReceiveChange | undefined;
|
|
63
|
+
/** The current mirrored core `Value` of a model. */
|
|
64
|
+
value(id: number): unknown;
|
|
65
|
+
ids(): number[];
|
|
66
|
+
/** Propose an edit to a mirrored model; returns the patch frame to send (encoded in this codec).
|
|
67
|
+
*
|
|
68
|
+
* Server-authoritative: the local mirror updates when the server echoes the authoritative patch
|
|
69
|
+
* back via `recv`, not optimistically.
|
|
70
|
+
*/
|
|
71
|
+
edit(id: number, value: unknown): string | Uint8Array;
|
|
72
|
+
/** Connect to a transports server and mirror it. Returns the `WebSocket`.
|
|
73
|
+
*
|
|
74
|
+
* On a reconnect (this client already mirrors models) it appends `?since=` with its last-seen rev per
|
|
75
|
+
* model, so the server replays only the delta instead of re-sending each whole model.
|
|
76
|
+
*/
|
|
77
|
+
connect(url: string): WebSocket;
|
|
78
|
+
/** Connect and mirror, **reconnecting** whenever the socket drops — so the client survives a server
|
|
79
|
+
* restart or a refresh. `authority` decides reconciliation on each (re)connect:
|
|
80
|
+
*
|
|
81
|
+
* - `"server"` (default): the server is canonical; the client adopts its state (resuming via `?since=`
|
|
82
|
+
* when it can, else a fresh snapshot) — the "refetch on refresh" behavior.
|
|
83
|
+
* - `"client"`: the client is canonical; after the server's snapshot it pushes its last-known state
|
|
84
|
+
* back as an edit, rectifying a server that came back stale/empty (merges under a CRDT, else
|
|
85
|
+
* overwrites).
|
|
86
|
+
*
|
|
87
|
+
* `onMessage` fires after each applied frame (e.g. to re-render). Returns `{ stop() }`.
|
|
88
|
+
*/
|
|
89
|
+
run(url: string, opts?: {
|
|
90
|
+
authority?: "server" | "client";
|
|
91
|
+
retry?: number;
|
|
92
|
+
onMessage?: () => void;
|
|
93
|
+
}): {
|
|
94
|
+
stop: () => void;
|
|
95
|
+
};
|
|
96
|
+
/** Mirror a server over Server-Sent Events (receive-only, JSON). Returns the `EventSource`. */
|
|
97
|
+
connectSSE(url: string): EventSource;
|
|
98
|
+
}
|
|
99
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Custom wire codec registry (the JS analog of `transports.register_codec` in Python).
|
|
2
|
+
*
|
|
3
|
+
* A codec turns a JSON-able object (a protocol message, or a model `Value`) into a wire frame
|
|
4
|
+
* (`string` or `Uint8Array`) and back. Register a matching implementation here for any content type
|
|
5
|
+
* you also register on the server, then use it via `new Client(contentType)` / `?codec=`.
|
|
6
|
+
*/
|
|
7
|
+
export type CodecEncode = (obj: unknown) => string | Uint8Array;
|
|
8
|
+
export type CodecDecode = (data: string | Uint8Array) => unknown;
|
|
9
|
+
/** Register a custom codec under `contentType`. The built-in json/msgpack codecs cannot be overridden. */
|
|
10
|
+
export declare function registerCodec(contentType: string, encode: CodecEncode, decode: CodecDecode): void;
|
|
11
|
+
/** Remove a previously registered custom codec. */
|
|
12
|
+
export declare function unregisterCodec(contentType: string): void;
|
|
13
|
+
/** The currently registered custom codec for `contentType`, if any. */
|
|
14
|
+
export declare function codecFor(contentType: string): {
|
|
15
|
+
encode: CodecEncode;
|
|
16
|
+
decode: CodecDecode;
|
|
17
|
+
} | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as wasm from "../../dist/pkg/transports";
|
|
2
|
+
export * as wasm from "../../dist/pkg/transports";
|
|
3
|
+
export declare const placeholder = "";
|
|
4
|
+
/** Diff two JSON-encoded models, returning the JSON-encoded patch. */
|
|
5
|
+
export declare const diff: (oldModel: string, newModel: string) => string;
|
|
6
|
+
/** Apply a JSON-encoded patch to a JSON-encoded model, returning the JSON-encoded result. */
|
|
7
|
+
export declare const apply: (model: string, patch: string) => string;
|
|
8
|
+
/** Encode a JSON-encoded model to codec bytes. */
|
|
9
|
+
export declare const encode: (model: string) => Uint8Array;
|
|
10
|
+
/** Decode codec bytes back to a JSON-encoded model string. */
|
|
11
|
+
export declare const decode: (bytes: Uint8Array) => string;
|
|
12
|
+
/** Encode a JSON-encoded model with the codec named by `codec` (e.g. "application/msgpack"). */
|
|
13
|
+
export declare const encodeAs: (model: string, codec: string) => Uint8Array;
|
|
14
|
+
/** Decode bytes (from `codec`'s codec) back to a JSON-encoded model string. */
|
|
15
|
+
export declare const decodeAs: (bytes: Uint8Array, codec: string) => string;
|
|
16
|
+
/** Convert an arbitrary JSON document to MessagePack bytes (for whole protocol messages). */
|
|
17
|
+
export declare const jsonToMsgpack: (json: string) => Uint8Array;
|
|
18
|
+
/** Convert MessagePack bytes back to a JSON document. */
|
|
19
|
+
export declare const msgpackToJson: (bytes: Uint8Array) => string;
|
|
20
|
+
/** Convert an arbitrary JSON document to CBOR bytes (for whole protocol messages). */
|
|
21
|
+
export declare const jsonToCbor: (json: string) => Uint8Array;
|
|
22
|
+
/** Convert CBOR bytes back to a JSON document. */
|
|
23
|
+
export declare const cborToJson: (bytes: Uint8Array) => string;
|
|
24
|
+
/** In-process model store: host / mutate → patch / apply / snapshot. */
|
|
25
|
+
export declare const Store: typeof wasm.Store;
|
|
26
|
+
export { toValue, fromValue } from "./bridge";
|
|
27
|
+
export type { Value } from "./bridge";
|
|
28
|
+
export { Client } from "./client";
|
|
29
|
+
export type { ModelPatch, PatchOp, PathSeg, ReceiveChange } from "./client";
|
|
30
|
+
export { registerCodec, unregisterCodec, codecFor } from "./codecs";
|
|
31
|
+
export type { CodecEncode, CodecDecode } from "./codecs";
|
package/dist/pkg/transports.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export class Store {
|
|
|
31
31
|
*/
|
|
32
32
|
export function apply(value: string, patch: string): string;
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Convert CBOR bytes back to a JSON document.
|
|
36
|
+
*/
|
|
37
|
+
export function cbor_to_json(data: Uint8Array): string;
|
|
38
|
+
|
|
34
39
|
/**
|
|
35
40
|
* Decode codec bytes back to a JSON-encoded model string.
|
|
36
41
|
*/
|
|
@@ -56,6 +61,11 @@ export function encode(value: string): Uint8Array;
|
|
|
56
61
|
*/
|
|
57
62
|
export function encode_as(value: string, codec: string): Uint8Array;
|
|
58
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Convert an arbitrary JSON document to CBOR bytes (a `Uint8Array` in JS).
|
|
66
|
+
*/
|
|
67
|
+
export function json_to_cbor(json: string): Uint8Array;
|
|
68
|
+
|
|
59
69
|
/**
|
|
60
70
|
* Convert an arbitrary JSON document to MessagePack bytes (a `Uint8Array` in JS).
|
|
61
71
|
*/
|
|
@@ -70,20 +80,22 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
|
|
|
70
80
|
|
|
71
81
|
export interface InitOutput {
|
|
72
82
|
readonly memory: WebAssembly.Memory;
|
|
73
|
-
readonly
|
|
83
|
+
readonly __wbg_store_free: (a: number, b: number) => void;
|
|
74
84
|
readonly apply: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
75
|
-
readonly
|
|
85
|
+
readonly cbor_to_json: (a: number, b: number) => [number, number, number, number];
|
|
76
86
|
readonly decode: (a: number, b: number) => [number, number, number, number];
|
|
77
|
-
readonly encode_as: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
78
87
|
readonly decode_as: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
88
|
+
readonly diff: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
89
|
+
readonly encode: (a: number, b: number) => [number, number, number, number];
|
|
90
|
+
readonly encode_as: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
91
|
+
readonly json_to_cbor: (a: number, b: number) => [number, number, number, number];
|
|
79
92
|
readonly json_to_msgpack: (a: number, b: number) => [number, number, number, number];
|
|
80
93
|
readonly msgpack_to_json: (a: number, b: number) => [number, number, number, number];
|
|
81
|
-
readonly
|
|
82
|
-
readonly store_new: () => number;
|
|
94
|
+
readonly store_apply: (a: number, b: bigint, c: number, d: number) => [number, number, number];
|
|
83
95
|
readonly store_host: (a: number, b: number, c: number, d: number, e: number) => [bigint, number, number];
|
|
84
|
-
readonly store_snapshot: (a: number, b: bigint) => [number, number];
|
|
85
96
|
readonly store_mutate: (a: number, b: bigint, c: number, d: number) => [number, number, number, number];
|
|
86
|
-
readonly
|
|
97
|
+
readonly store_new: () => number;
|
|
98
|
+
readonly store_snapshot: (a: number, b: bigint) => [number, number];
|
|
87
99
|
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
88
100
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
89
101
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
package/dist/pkg/transports.js
CHANGED
|
@@ -118,6 +118,32 @@ export function apply(value, patch) {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Convert CBOR bytes back to a JSON document.
|
|
123
|
+
* @param {Uint8Array} data
|
|
124
|
+
* @returns {string}
|
|
125
|
+
*/
|
|
126
|
+
export function cbor_to_json(data) {
|
|
127
|
+
let deferred3_0;
|
|
128
|
+
let deferred3_1;
|
|
129
|
+
try {
|
|
130
|
+
const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
|
|
131
|
+
const len0 = WASM_VECTOR_LEN;
|
|
132
|
+
const ret = wasm.cbor_to_json(ptr0, len0);
|
|
133
|
+
var ptr2 = ret[0];
|
|
134
|
+
var len2 = ret[1];
|
|
135
|
+
if (ret[3]) {
|
|
136
|
+
ptr2 = 0; len2 = 0;
|
|
137
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
138
|
+
}
|
|
139
|
+
deferred3_0 = ptr2;
|
|
140
|
+
deferred3_1 = len2;
|
|
141
|
+
return getStringFromWasm0(ptr2, len2);
|
|
142
|
+
} finally {
|
|
143
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
121
147
|
/**
|
|
122
148
|
* Decode codec bytes back to a JSON-encoded model string.
|
|
123
149
|
* @param {Uint8Array} data
|
|
@@ -239,6 +265,23 @@ export function encode_as(value, codec) {
|
|
|
239
265
|
return v3;
|
|
240
266
|
}
|
|
241
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Convert an arbitrary JSON document to CBOR bytes (a `Uint8Array` in JS).
|
|
270
|
+
* @param {string} json
|
|
271
|
+
* @returns {Uint8Array}
|
|
272
|
+
*/
|
|
273
|
+
export function json_to_cbor(json) {
|
|
274
|
+
const ptr0 = passStringToWasm0(json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
275
|
+
const len0 = WASM_VECTOR_LEN;
|
|
276
|
+
const ret = wasm.json_to_cbor(ptr0, len0);
|
|
277
|
+
if (ret[3]) {
|
|
278
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
279
|
+
}
|
|
280
|
+
var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
281
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
282
|
+
return v2;
|
|
283
|
+
}
|
|
284
|
+
|
|
242
285
|
/**
|
|
243
286
|
* Convert an arbitrary JSON document to MessagePack bytes (a `Uint8Array` in JS).
|
|
244
287
|
* @param {string} json
|
|
@@ -284,11 +327,11 @@ export function msgpack_to_json(data) {
|
|
|
284
327
|
function __wbg_get_imports() {
|
|
285
328
|
const import0 = {
|
|
286
329
|
__proto__: null,
|
|
287
|
-
|
|
330
|
+
__wbg_Error_92b29b0548f8b746: function(arg0, arg1) {
|
|
288
331
|
const ret = Error(getStringFromWasm0(arg0, arg1));
|
|
289
332
|
return ret;
|
|
290
333
|
},
|
|
291
|
-
|
|
334
|
+
__wbg___wbindgen_throw_344f42d3211c4765: function(arg0, arg1) {
|
|
292
335
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
293
336
|
},
|
|
294
337
|
__wbindgen_init_externref_table: function() {
|
|
Binary file
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
/* tslint:disable */
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
export const memory: WebAssembly.Memory;
|
|
4
|
-
export const
|
|
4
|
+
export const __wbg_store_free: (a: number, b: number) => void;
|
|
5
5
|
export const apply: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
6
|
-
export const
|
|
6
|
+
export const cbor_to_json: (a: number, b: number) => [number, number, number, number];
|
|
7
7
|
export const decode: (a: number, b: number) => [number, number, number, number];
|
|
8
|
-
export const encode_as: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
9
8
|
export const decode_as: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
9
|
+
export const diff: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
10
|
+
export const encode: (a: number, b: number) => [number, number, number, number];
|
|
11
|
+
export const encode_as: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
12
|
+
export const json_to_cbor: (a: number, b: number) => [number, number, number, number];
|
|
10
13
|
export const json_to_msgpack: (a: number, b: number) => [number, number, number, number];
|
|
11
14
|
export const msgpack_to_json: (a: number, b: number) => [number, number, number, number];
|
|
12
|
-
export const
|
|
13
|
-
export const store_new: () => number;
|
|
15
|
+
export const store_apply: (a: number, b: bigint, c: number, d: number) => [number, number, number];
|
|
14
16
|
export const store_host: (a: number, b: number, c: number, d: number, e: number) => [bigint, number, number];
|
|
15
|
-
export const store_snapshot: (a: number, b: bigint) => [number, number];
|
|
16
17
|
export const store_mutate: (a: number, b: bigint, c: number, d: number) => [number, number, number, number];
|
|
17
|
-
export const
|
|
18
|
+
export const store_new: () => number;
|
|
19
|
+
export const store_snapshot: (a: number, b: bigint) => [number, number];
|
|
18
20
|
export const __wbindgen_externrefs: WebAssembly.Table;
|
|
19
21
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
20
22
|
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1kbgz/transports",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Generic communication library",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git@github.com:1kbgz/transports.git"
|
|
8
|
+
},
|
|
6
9
|
"author": "1kbgz <dev@1kbgz.com>",
|
|
7
10
|
"license": "Apache-2.0",
|
|
8
11
|
"type": "module",
|
|
@@ -25,29 +28,30 @@
|
|
|
25
28
|
"access": "public"
|
|
26
29
|
},
|
|
27
30
|
"devDependencies": {
|
|
28
|
-
"@playwright/test": "^1.
|
|
31
|
+
"@playwright/test": "^1.62.0",
|
|
29
32
|
"cpy": "^13.2.2",
|
|
30
|
-
"esbuild": "^0.28.
|
|
31
|
-
"lightningcss": "^1.
|
|
33
|
+
"esbuild": "^0.28.1",
|
|
34
|
+
"lightningcss": "^1.33.0",
|
|
32
35
|
"http-server": "^14.1.1",
|
|
33
36
|
"nodemon": "^3.1.14",
|
|
34
37
|
"npm-run-all": "^4.1.5",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
38
|
+
"oxfmt": "^0.63.0",
|
|
39
|
+
"oxlint": "^1.78.0",
|
|
40
|
+
"typescript": "^7.0.2"
|
|
37
41
|
},
|
|
38
42
|
"scripts": {
|
|
39
|
-
"setup": "cargo install -f wasm-bindgen-cli --version 0.2.
|
|
43
|
+
"setup": "cargo install -f wasm-bindgen-cli --version 0.2.126 --locked",
|
|
40
44
|
"build:debug": "node build.mjs --debug",
|
|
41
|
-
"build:rust": "cargo build --release --all-features --target wasm32-unknown-unknown",
|
|
45
|
+
"build:rust": "cargo build --release --all-features --target wasm32-unknown-unknown --target-dir ../target",
|
|
42
46
|
"build:wasm-bindgen": "wasm-bindgen ../target/wasm32-unknown-unknown/release/transports.wasm --out-dir ./dist/pkg --target web",
|
|
43
47
|
"build:prod": "node build.mjs",
|
|
44
48
|
"build": "npm-run-all build:rust build:wasm-bindgen build:prod",
|
|
45
49
|
"clean": "rm -rf dist lib playwright-report ../transports/extension",
|
|
46
50
|
"dev": "npm-run-all -p start watch",
|
|
47
|
-
"lint:js": "
|
|
51
|
+
"lint:js": "oxlint . && oxfmt --check \"src/**/*.{js,ts,jsx,tsx,css}\" \"tests/**/*.{js,ts,jsx,tsx}\" \"*.mjs\" \"*.json\"",
|
|
48
52
|
"lint:rust": "cargo clippy --all-features && cargo fmt --all -- --check",
|
|
49
53
|
"lint": "npm-run-all lint:*",
|
|
50
|
-
"fix:js": "
|
|
54
|
+
"fix:js": "oxlint --fix . && oxfmt --write \"src/**/*.{js,ts,jsx,tsx,css}\" \"tests/**/*.{js,ts,jsx,tsx}\" \"*.mjs\" \"*.json\"",
|
|
51
55
|
"fix:rust": "cargo fmt --all",
|
|
52
56
|
"fix": "npm-run-all fix:*",
|
|
53
57
|
"preinstall": "npx only-allow pnpm",
|
package/dist/html/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<html lang="en">
|
|
2
|
-
<head>
|
|
3
|
-
<meta charset="UTF-8" />
|
|
4
|
-
<title>transports</title>
|
|
5
|
-
<meta name="description" content="Generic communication library" />
|
|
6
|
-
<meta name="author" content="1kbgz" />
|
|
7
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
-
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
|
9
|
-
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
|
10
|
-
<script type="module" src="cdn/index.js"></script>
|
|
11
|
-
</head>
|
|
12
|
-
<body></body>
|
|
13
|
-
</html>
|