@openlv/core 0.1.1 → 0.2.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/encryption/asymmetric.js +1 -1
- package/dist/encryption/asymmetric.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +1 -1
- package/dist/log.js +5 -5
- package/dist/log.js.map +1 -1
- package/dist/observable.d.ts +15 -0
- package/dist/observable.js +36 -0
- package/dist/observable.js.map +1 -0
- package/dist/session.js.map +1 -1
- package/dist/url/index.js +13 -13
- package/dist/url/index.js.map +1 -1
- package/dist/url/index.test.js +4 -4
- package/dist/url/index.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -66,7 +66,7 @@ export const generateKeyPair = async () => {
|
|
|
66
66
|
};
|
|
67
67
|
export const initEncryptionKeys = async (initParameters) => {
|
|
68
68
|
const { encryptionKey, decryptionKey } = await generateKeyPair();
|
|
69
|
-
// Note: the peer key is deliberately left undefined until discovered
|
|
69
|
+
// Note: the peer key is deliberately left undefined until discovered --
|
|
70
70
|
// falling back to our own key would silently encrypt to ourselves.
|
|
71
71
|
const publicKey = initParameters && "publicKey" in initParameters
|
|
72
72
|
? initParameters.publicKey
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asymmetric.js","sourceRoot":"","sources":["../../src/encryption/asymmetric.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"asymmetric.js","sourceRoot":"","sources":["../../src/encryption/asymmetric.ts"],"names":[],"mappings":"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","sourcesContent":["import { secretbox } from \"@noble/ciphers/salsa.js\";\nimport { x25519 } from \"@noble/curves/ed25519.js\";\n\nimport type { SessionLinkParameters } from \"../session.js\";\nimport { fromBase64, toBase64 } from \"./base64.js\";\n\nconst PUBLIC_KEY_BYTES = 32;\nconst NONCE_BYTES = 24;\n\nconst composePayload = (\n ephemeralPublicKey: Uint8Array,\n nonce: Uint8Array,\n ciphertext: Uint8Array,\n): string => {\n const payload = new Uint8Array(\n ephemeralPublicKey.length + nonce.length + ciphertext.length,\n );\n\n payload.set(ephemeralPublicKey, 0);\n payload.set(nonce, ephemeralPublicKey.length);\n payload.set(ciphertext, ephemeralPublicKey.length + nonce.length);\n\n return toBase64(payload);\n};\n\nconst decomposePayload = (\n payload: string,\n): {\n ephemeralPublicKey: Uint8Array;\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n} => {\n const bytes = fromBase64(payload);\n\n if (bytes.length <= PUBLIC_KEY_BYTES + NONCE_BYTES) {\n throw new Error(\"Encrypted payload is malformed.\");\n }\n\n const ephemeralPublicKey = bytes.slice(0, PUBLIC_KEY_BYTES);\n const nonce = bytes.slice(PUBLIC_KEY_BYTES, PUBLIC_KEY_BYTES + NONCE_BYTES);\n const ciphertext = bytes.slice(PUBLIC_KEY_BYTES + NONCE_BYTES);\n\n return {\n ephemeralPublicKey,\n nonce,\n ciphertext,\n };\n};\n\nexport type EncryptionKey = {\n toString: () => string;\n encrypt: (message: string) => Promise<string>;\n};\n\nexport type DecryptionKey = {\n toString: () => string;\n decrypt: (message: string) => Promise<string>;\n};\n\nexport const createEncryptionKey = async (\n publicKey: Uint8Array,\n): Promise<EncryptionKey> => {\n if (publicKey.length !== PUBLIC_KEY_BYTES) {\n throw new Error(\n `Public key must be ${PUBLIC_KEY_BYTES} bytes, got ${publicKey.length}`,\n );\n }\n\n const serializedPublicKey = toBase64(publicKey);\n\n return {\n toString: () => serializedPublicKey,\n encrypt: async (message: string) => {\n const ephemeralSecret = x25519.utils.randomSecretKey();\n const ephemeralPublic = x25519.getPublicKey(ephemeralSecret);\n const nonce = crypto.getRandomValues(new Uint8Array(NONCE_BYTES));\n const { seal } = secretbox(\n x25519.getSharedSecret(ephemeralSecret, publicKey),\n nonce,\n );\n const ciphertext = seal(new TextEncoder().encode(message));\n\n return composePayload(ephemeralPublic, nonce, ciphertext);\n },\n };\n};\n\nexport const parseEncryptionKey = async (\n serializedKey: string,\n): Promise<EncryptionKey> => createEncryptionKey(fromBase64(serializedKey));\n\nexport const createDecryptionKey = async (\n secretKey: Uint8Array,\n): Promise<DecryptionKey> => {\n const serializedSecretKey = toBase64(secretKey);\n\n return {\n toString: () => serializedSecretKey,\n decrypt: async (message: string) => {\n if (!message) {\n throw new Error(\"Cannot decrypt an empty payload\");\n }\n\n const { ephemeralPublicKey, nonce, ciphertext }\n = decomposePayload(message);\n const { open } = secretbox(\n x25519.getSharedSecret(secretKey, ephemeralPublicKey),\n nonce,\n );\n\n return new TextDecoder().decode(open(ciphertext));\n },\n };\n};\n\nexport type EncKeypair = {\n encryptionKey: EncryptionKey;\n decryptionKey: DecryptionKey;\n};\n\nexport const generateKeyPair = async (): Promise<EncKeypair> => {\n const secretKey = x25519.utils.randomSecretKey();\n const publicKey = x25519.getPublicKey(secretKey);\n\n return {\n encryptionKey: await createEncryptionKey(publicKey),\n decryptionKey: await createDecryptionKey(secretKey),\n };\n};\n\nexport const initEncryptionKeys = async (\n initParameters?: SessionLinkParameters,\n): Promise<EncKeypair & { relyingEncryptionKey?: EncryptionKey; }> => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n\n // Note: the peer key is deliberately left undefined until discovered --\n // falling back to our own key would silently encrypt to ourselves.\n const publicKey\n = initParameters && \"publicKey\" in initParameters\n ? (initParameters.publicKey as string)\n : undefined;\n\n return {\n encryptionKey,\n decryptionKey,\n relyingEncryptionKey: publicKey\n ? await parseEncryptionKey(publicKey)\n : undefined,\n };\n};\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC","sourcesContent":["/** biome-ignore-all lint/performance/noReExportAll: package entrypoint */\n/** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */\n\nexport * from \"./log.js\";\nexport * from \"./make.js\";\nexport * from \"./session.js\";\nexport * from \"./url/index.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC","sourcesContent":["/** biome-ignore-all lint/performance/noReExportAll: package entrypoint */\n/** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */\n\nexport * from \"./log.js\";\nexport * from \"./make.js\";\nexport * from \"./observable.js\";\nexport * from \"./session.js\";\nexport * from \"./url/index.js\";\n"]}
|
package/dist/log.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const createLogger: (scope: string, color?: string) => (...
|
|
1
|
+
export declare const createLogger: (scope: string, color?: string) => (...arguments_: Parameters<typeof console.log>) => void;
|
package/dist/log.js
CHANGED
|
@@ -17,15 +17,15 @@ const isDebugEnabled = () => {
|
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
-
const
|
|
21
|
-
export const createLogger = (scope, color = "gray") => (...
|
|
20
|
+
const isSupportsColor = typeof document !== "undefined";
|
|
21
|
+
export const createLogger = (scope, color = "gray") => (...arguments_) => {
|
|
22
22
|
if (!isDebugEnabled())
|
|
23
23
|
return;
|
|
24
|
-
if (
|
|
25
|
-
console.log(`%c[${scope}]%c`, `color: ${color}; font-weight: bold`, "color: inherit; font-weight: normal", ...
|
|
24
|
+
if (isSupportsColor) {
|
|
25
|
+
console.log(`%c[${scope}]%c`, `color: ${color}; font-weight: bold`, "color: inherit; font-weight: normal", ...arguments_);
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
|
-
console.log(`[${scope}]`, ...
|
|
28
|
+
console.log(`[${scope}]`, ...arguments_);
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,GAAY,EAAE;IACnC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,UAIT,CAAC;QAEF,OAAO,OAAO,CACZ,CAAC,CAAC,YAAY;eACX,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC;eACvC,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,CACpC,CAAC;IACJ,CAAC;IACD,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,GAAY,EAAE;IACnC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,UAIT,CAAC;QAEF,OAAO,OAAO,CACZ,CAAC,CAAC,YAAY;eACX,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC;eACvC,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,CACpC,CAAC;IACJ,CAAC;IACD,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAC;AAExD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,KAAK,GAAG,MAAM,EAAE,EAAE,CAC5D,CAAC,GAAG,UAA0C,EAAE,EAAE;IAChD,IAAI,CAAC,cAAc,EAAE;QAAE,OAAO;IAE9B,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CACT,MAAM,KAAK,KAAK,EAChB,UAAU,KAAK,qBAAqB,EACpC,qCAAqC,EACrC,GAAG,UAAU,CACd,CAAC;IACJ,CAAC;SACI,CAAC;QACJ,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC","sourcesContent":["/**\n * Debug logging is disabled by default so that key material, handshake\n * traffic, and RPC payloads never reach the console in production.\n *\n * Enable it by setting `globalThis.OPENLV_DEBUG = true`, adding an\n * `openlv:debug` key to localStorage, or setting the `OPENLV_DEBUG`\n * environment variable (Node).\n */\nconst isDebugEnabled = (): boolean => {\n try {\n const g = globalThis as {\n OPENLV_DEBUG?: unknown;\n localStorage?: Storage;\n process?: { env?: Record<string, string | undefined>; };\n };\n\n return Boolean(\n g.OPENLV_DEBUG\n ?? g.localStorage?.getItem(\"openlv:debug\")\n ?? g.process?.env?.[\"OPENLV_DEBUG\"],\n );\n }\n catch {\n return false;\n }\n};\n\nconst isSupportsColor = typeof document !== \"undefined\";\n\nexport const createLogger = (scope: string, color = \"gray\") =>\n (...arguments_: Parameters<typeof console.log>) => {\n if (!isDebugEnabled()) return;\n\n if (isSupportsColor) {\n console.log(\n `%c[${scope}]%c`,\n `color: ${color}; font-weight: bold`,\n \"color: inherit; font-weight: normal\",\n ...arguments_,\n );\n }\n else {\n console.log(`[${scope}]`, ...arguments_);\n }\n };\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Unsubscribe = () => void;
|
|
2
|
+
export type Observable<T> = {
|
|
3
|
+
get(): T;
|
|
4
|
+
/**
|
|
5
|
+
* Calls the listener synchronously with the current value, then on every
|
|
6
|
+
* change. The replay makes subscribe-then-check patterns race-free and
|
|
7
|
+
* matches what reactive consumers (e.g. Solid's `from`) expect.
|
|
8
|
+
*/
|
|
9
|
+
subscribe(listener: (value: T) => void): Unsubscribe;
|
|
10
|
+
/** Resolves with the first value (including the current one) that matches. */
|
|
11
|
+
until(isMatch: (value: T) => boolean): Promise<T>;
|
|
12
|
+
};
|
|
13
|
+
/** Returns false when the value was already equal and no listener ran. */
|
|
14
|
+
export type Setter<T> = (value: T) => boolean;
|
|
15
|
+
export declare const observable: <T>(initial: T) => [Observable<T>, Setter<T>];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const observable = (initial) => {
|
|
2
|
+
let value = initial;
|
|
3
|
+
const listeners = new Set();
|
|
4
|
+
const get = () => value;
|
|
5
|
+
const subscribe = (listener) => {
|
|
6
|
+
listeners.add(listener);
|
|
7
|
+
listener(value);
|
|
8
|
+
return () => listeners.delete(listener);
|
|
9
|
+
};
|
|
10
|
+
const until = (isMatch) => {
|
|
11
|
+
if (isMatch(value))
|
|
12
|
+
return Promise.resolve(value);
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const unsubscribe = subscribe((next) => {
|
|
15
|
+
// The replay on subscribe re-delivers the current value; it cannot
|
|
16
|
+
// match, because the fast path above already checked it.
|
|
17
|
+
if (!isMatch(next))
|
|
18
|
+
return;
|
|
19
|
+
resolve(next);
|
|
20
|
+
unsubscribe();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
return [
|
|
25
|
+
{ get, subscribe, until },
|
|
26
|
+
(next) => {
|
|
27
|
+
if (value === next)
|
|
28
|
+
return false;
|
|
29
|
+
value = next;
|
|
30
|
+
for (const listener of listeners)
|
|
31
|
+
listener(value);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=observable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observable.js","sourceRoot":"","sources":["../src/observable.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAI,OAAU,EAA8B,EAAE;IACtE,IAAI,KAAK,GAAG,OAAO,CAAC;IACpB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEhD,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC;IAExB,MAAM,SAAS,GAAG,CAAC,QAA4B,EAAe,EAAE;QAC9D,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,QAAQ,CAAC,KAAK,CAAC,CAAC;QAEhB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,OAA8B,EAAc,EAAE;QAC3D,IAAI,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAElD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;gBACrC,mEAAmE;gBACnE,yDAAyD;gBACzD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;oBAAE,OAAO;gBAE3B,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,WAAW,EAAE,CAAC;YAChB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO;QACL,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE;QACzB,CAAC,IAAI,EAAE,EAAE;YACP,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YAEjC,KAAK,GAAG,IAAI,CAAC;YAEb,KAAK,MAAM,QAAQ,IAAI,SAAS;gBAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;YAElD,OAAO,IAAI,CAAC;QACd,CAAC;KAAC,CAAC;AACP,CAAC,CAAC","sourcesContent":["export type Unsubscribe = () => void;\nexport type Observable<T> = {\n get(): T;\n /**\n * Calls the listener synchronously with the current value, then on every\n * change. The replay makes subscribe-then-check patterns race-free and\n * matches what reactive consumers (e.g. Solid's `from`) expect.\n */\n subscribe(listener: (value: T) => void): Unsubscribe;\n /** Resolves with the first value (including the current one) that matches. */\n until(isMatch: (value: T) => boolean): Promise<T>;\n};\n/** Returns false when the value was already equal and no listener ran. */\nexport type Setter<T> = (value: T) => boolean;\n\nexport const observable = <T>(initial: T): [Observable<T>, Setter<T>] => {\n let value = initial;\n const listeners = new Set<(value: T) => void>();\n\n const get = () => value;\n\n const subscribe = (listener: (value: T) => void): Unsubscribe => {\n listeners.add(listener);\n listener(value);\n\n return () => listeners.delete(listener);\n };\n\n const until = (isMatch: (value: T) => boolean): Promise<T> => {\n if (isMatch(value)) return Promise.resolve(value);\n\n return new Promise((resolve) => {\n const unsubscribe = subscribe((next) => {\n // The replay on subscribe re-delivers the current value; it cannot\n // match, because the fast path above already checked it.\n if (!isMatch(next)) return;\n\n resolve(next);\n unsubscribe();\n });\n });\n };\n\n return [\n { get, subscribe, until },\n (next) => {\n if (value === next) return false;\n\n value = next;\n\n for (const listener of listeners) listener(value);\n\n return true;\n }];\n};\n"]}
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"","sourcesContent":["import type { DecryptionKey, EncryptionKey } from \"./encryption/asymmetric.js\";\n\n// typescript type for 32 character hex string\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport type Hex<_L> = string;\n\n/**\n * The parameters from the connection url\n */\nexport type SessionHandshakeParameters = {\n // Version of the openlv protocol\n version: 1;\n // Unique session identifier\n sessionId: Hex<16>;\n // Hash of peerA's public key\n h: Hex<16>;\n // Shared key for symmetric encryption during handshake (32 hex chars = 16 bytes)\n k: Hex<32>;\n // Protocol for signaling\n p: string;\n // Signaling server URL\n s: string;\n};\n\n/**\n * The parameters that describe a session\n * these can be used to restart a session\n */\nexport type SessionConnectionParameters = SessionHandshakeParameters & {\n relyingPublicKey: EncryptionKey;\n // Our public key for the relying party to encrypt messages to us\n encryptionKey: EncryptionKey;\n // Our private key for decrypting messages from the relying party\n decryptionKey: DecryptionKey;\n};\n\nexport type SessionCreationParameters = Pick<\n SessionHandshakeParameters,\n \"p\" | \"s\"\n>;\n\nexport type SessionLinkParameters
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"","sourcesContent":["import type { DecryptionKey, EncryptionKey } from \"./encryption/asymmetric.js\";\n\n// typescript type for 32 character hex string\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport type Hex<_L> = string;\n\n/**\n * The parameters from the connection url\n */\nexport type SessionHandshakeParameters = {\n // Version of the openlv protocol\n version: 1;\n // Unique session identifier\n sessionId: Hex<16>;\n // Hash of peerA's public key\n h: Hex<16>;\n // Shared key for symmetric encryption during handshake (32 hex chars = 16 bytes)\n k: Hex<32>;\n // Protocol for signaling\n p: string;\n // Signaling server URL\n s: string;\n};\n\n/**\n * The parameters that describe a session\n * these can be used to restart a session\n */\nexport type SessionConnectionParameters = SessionHandshakeParameters & {\n relyingPublicKey: EncryptionKey;\n // Our public key for the relying party to encrypt messages to us\n encryptionKey: EncryptionKey;\n // Our private key for decrypting messages from the relying party\n decryptionKey: DecryptionKey;\n};\n\nexport type SessionCreationParameters = Pick<\n SessionHandshakeParameters,\n \"p\" | \"s\"\n>;\n\nexport type SessionLinkParameters\n = | SessionConnectionParameters\n | SessionHandshakeParameters\n | SessionCreationParameters;\n"]}
|
package/dist/url/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export const OPENLV_PROTOCOL_VERSION = 1;
|
|
2
2
|
export const encodeConnectionURL = (payload) => {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const parameters = new URLSearchParams();
|
|
4
|
+
parameters.set("h", payload.h);
|
|
5
|
+
parameters.set("k", payload.k);
|
|
6
6
|
if (payload.p) {
|
|
7
|
-
|
|
7
|
+
parameters.set("p", payload.p);
|
|
8
8
|
}
|
|
9
9
|
if (payload.s) {
|
|
10
|
-
|
|
10
|
+
parameters.set("s", payload.s);
|
|
11
11
|
}
|
|
12
|
-
return `openlv://${payload.sessionId}@${OPENLV_PROTOCOL_VERSION}?${
|
|
12
|
+
return `openlv://${payload.sessionId}@${OPENLV_PROTOCOL_VERSION}?${parameters.toString()}`;
|
|
13
13
|
};
|
|
14
14
|
export const decodeConnectionURL = (url) => {
|
|
15
15
|
if (typeof url !== "string")
|
|
@@ -19,16 +19,16 @@ export const decodeConnectionURL = (url) => {
|
|
|
19
19
|
if (!url.startsWith("openlv://"))
|
|
20
20
|
throw new Error(`Invalid URL format: must start with 'openlv://', got: ${url}`);
|
|
21
21
|
try {
|
|
22
|
-
const
|
|
23
|
-
const sessionId =
|
|
24
|
-
const version = Number(
|
|
22
|
+
const urlObject = new URL(url);
|
|
23
|
+
const sessionId = urlObject.username;
|
|
24
|
+
const version = Number(urlObject.hostname || urlObject.pathname.replace("/", ""));
|
|
25
25
|
if (version !== OPENLV_PROTOCOL_VERSION) {
|
|
26
26
|
throw new Error(`Invalid protocol version: expected ${OPENLV_PROTOCOL_VERSION}, got ${version}`);
|
|
27
27
|
}
|
|
28
|
-
const h =
|
|
29
|
-
const k =
|
|
30
|
-
const s =
|
|
31
|
-
const p =
|
|
28
|
+
const h = urlObject.searchParams.get("h") || "";
|
|
29
|
+
const k = urlObject.searchParams.get("k") || "";
|
|
30
|
+
const s = urlObject.searchParams.get("s") || undefined;
|
|
31
|
+
const p = urlObject.searchParams.get("p") || "mqtt";
|
|
32
32
|
if (!sessionId) {
|
|
33
33
|
throw new Error("Session ID is required in URL");
|
|
34
34
|
}
|
package/dist/url/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAmC,EAAE,EAAE;IACzE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionHandshakeParameters } from \"../session.js\";\n\nexport const OPENLV_PROTOCOL_VERSION = 1;\n\nexport const encodeConnectionURL = (payload: SessionHandshakeParameters) => {\n const parameters = new URLSearchParams();\n\n parameters.set(\"h\", payload.h);\n parameters.set(\"k\", payload.k);\n\n if (payload.p) {\n parameters.set(\"p\", payload.p);\n }\n\n if (payload.s) {\n parameters.set(\"s\", payload.s);\n }\n\n return `openlv://${payload.sessionId}@${OPENLV_PROTOCOL_VERSION}?${parameters.toString()}`;\n};\n\nexport const decodeConnectionURL = (\n url: string,\n): SessionHandshakeParameters => {\n if (typeof url !== \"string\") throw new TypeError(`Invalid URL type: expected string, got ${typeof url}`);\n\n if (!url || url.trim() === \"\") throw new Error(\"URL cannot be empty\");\n\n if (!url.startsWith(\"openlv://\")) throw new Error(\n `Invalid URL format: must start with 'openlv://', got: ${url}`,\n );\n\n try {\n const urlObject = new URL(url);\n const sessionId = urlObject.username;\n const version = Number(urlObject.hostname || urlObject.pathname.replace(\"/\", \"\"));\n\n if (version !== OPENLV_PROTOCOL_VERSION) {\n throw new Error(\n `Invalid protocol version: expected ${OPENLV_PROTOCOL_VERSION}, got ${version}`,\n );\n }\n\n const h = urlObject.searchParams.get(\"h\") || \"\";\n const k = urlObject.searchParams.get(\"k\") || \"\";\n const s = urlObject.searchParams.get(\"s\") || undefined;\n\n const p = urlObject.searchParams.get(\"p\") || \"mqtt\";\n\n if (!sessionId) {\n throw new Error(\"Session ID is required in URL\");\n }\n\n // Validate session ID format (16 characters, URL-safe alphabet)\n if (!/^[A-Za-z0-9_-]{16}$/.test(sessionId)) {\n throw new Error(\n \"Invalid session ID format: must be 16 URL-safe characters\",\n );\n }\n\n if (!h) {\n throw new Error(\"Public key hash (h parameter) is required in URL\");\n }\n\n // Validate public key hash format (16 hex characters)\n if (!/^[0-9a-f]{16}$/.test(h)) {\n throw new Error(\n \"Invalid public key hash format: must be 16 hex characters\",\n );\n }\n\n if (!k) {\n throw new Error(\"Shared key (k parameter) is required in URL\");\n }\n\n // Validate shared key format (32 hex characters)\n if (!/^[0-9a-f]{32}$/.test(k)) {\n throw new Error(\"Invalid shared key format: must be 32 lowercase hex characters\");\n }\n\n return {\n version,\n sessionId,\n h,\n k,\n s: s ?? \"\",\n p,\n };\n }\n catch (error) {\n const errorMessage\n = error instanceof Error ? error.message : \"Unknown error\";\n\n // Strip query string so handshake secrets (k) are not exposed in error messages\n const safeUrl = url.includes(\"?\") ? url.slice(0, url.indexOf(\"?\")) + \"?[redacted]\" : url;\n\n throw new Error(`Failed to parse URL: ${safeUrl}. Original error: ${errorMessage}`);\n }\n};\n"]}
|
package/dist/url/index.test.js
CHANGED
|
@@ -15,16 +15,16 @@ const testCases = [
|
|
|
15
15
|
];
|
|
16
16
|
describe("URL Utilities", () => {
|
|
17
17
|
test("should decode connection URL", () => {
|
|
18
|
-
|
|
18
|
+
for (const testCase of testCases) {
|
|
19
19
|
const result = decodeConnectionURL(testCase.url);
|
|
20
20
|
expect(result).toEqual(testCase.expected);
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
22
|
});
|
|
23
23
|
test("should encode connection URL", () => {
|
|
24
|
-
|
|
24
|
+
for (const testCase of testCases) {
|
|
25
25
|
const result = encodeConnectionURL(testCase.expected);
|
|
26
26
|
expect(result).toEqual(testCase.url);
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
//# sourceMappingURL=index.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/url/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,SAAS,GAAG;IAChB;QACE,GAAG,EAAE,yIAAyI;QAC9I,QAAQ,EAAE;YACR,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,kBAAkB;YAC7B,CAAC,EAAE,kBAAkB;YACrB,CAAC,EAAE,kCAAkC;YACrC,CAAC,EAAE,MAAM;YACT,CAAC,EAAE,oCAAoC;SAC/B;KACX;CACF,CAAC;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACxC,
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/url/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,SAAS,GAAG;IAChB;QACE,GAAG,EAAE,yIAAyI;QAC9I,QAAQ,EAAE;YACR,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,kBAAkB;YAC7B,CAAC,EAAE,kBAAkB;YACrB,CAAC,EAAE,kCAAkC;YACrC,CAAC,EAAE,MAAM;YACT,CAAC,EAAE,oCAAoC;SAC/B;KACX;CACF,CAAC;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACxC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAEjD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACxC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEtD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test } from \"vitest\";\n\nimport { decodeConnectionURL, encodeConnectionURL } from \"./index.js\";\n\nconst testCases = [\n {\n url: \"openlv://k7n8m9x2w5q1p3r6@1?h=a1b2c3d4e5f60708&k=0123456789abcdef0123456789abcdef&p=mqtt&s=wss%3A%2F%2Ftest.mosquitto.org%3A8081%2Fmqtt\",\n expected: {\n version: 1,\n sessionId: \"k7n8m9x2w5q1p3r6\",\n h: \"a1b2c3d4e5f60708\",\n k: \"0123456789abcdef0123456789abcdef\",\n p: \"mqtt\",\n s: \"wss://test.mosquitto.org:8081/mqtt\",\n } as const,\n },\n];\n\ndescribe(\"URL Utilities\", () => {\n test(\"should decode connection URL\", () => {\n for (const testCase of testCases) {\n const result = decodeConnectionURL(testCase.url);\n\n expect(result).toEqual(testCase.expected);\n }\n });\n\n test(\"should encode connection URL\", () => {\n for (const testCase of testCases) {\n const result = encodeConnectionURL(testCase.expected);\n\n expect(result).toEqual(testCase.url);\n }\n });\n});\n"]}
|