@quilted/threads
Advanced tools
Comparing version 0.1.7 to 0.1.8
import type { ThreadEncodingStrategy, ThreadEncodingStrategyApi } from '../types'; | ||
export declare function createBasicEncoder(api: ThreadEncodingStrategyApi): ThreadEncodingStrategy; | ||
import type { MemoryRetainer } from '../memory'; | ||
export declare function createBasicEncoderWithOverrides({ encode: encodeOverride, decode: decodeOverride, }?: { | ||
encode?(value: unknown, api: ThreadEncodingStrategyApi & Pick<ThreadEncodingStrategy, 'encode'>): ReturnType<ThreadEncodingStrategy['encode']> | undefined; | ||
decode?(value: unknown, retainedBy: Iterable<MemoryRetainer> | undefined, api: ThreadEncodingStrategyApi & Pick<ThreadEncodingStrategy, 'decode'>): unknown; | ||
}): (api: ThreadEncodingStrategyApi) => ThreadEncodingStrategy; | ||
export declare const createBasicEncoder: (api: ThreadEncodingStrategyApi) => ThreadEncodingStrategy; | ||
//# sourceMappingURL=basic.d.ts.map |
@@ -1,2 +0,2 @@ | ||
export { createBasicEncoder } from './basic'; | ||
export { createBasicEncoder, createBasicEncoderWithOverrides } from './basic'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -7,3 +7,3 @@ export { createThread } from './thread'; | ||
export { targetFromWebWorker, targetFromMessagePort, targetFromBrowserWebSocket, } from './targets'; | ||
export { createBasicEncoder } from './encoding'; | ||
export { createBasicEncoder, createBasicEncoderWithOverrides } from './encoding'; | ||
export { createThreadAbortSignal, acceptThreadAbortSignal } from './abort'; | ||
@@ -10,0 +10,0 @@ export type { ThreadAbortSignal } from './abort'; |
# @quilted/threads | ||
## 0.1.8 | ||
### Patch Changes | ||
- [`86587484`](https://github.com/lemonmade/quilt/commit/86587484846906e194bba956bbd338aa00544625) Thanks [@lemonmade](https://github.com/lemonmade)! - Add basic encoder overrides | ||
## 0.1.7 | ||
@@ -4,0 +10,0 @@ |
@@ -5,3 +5,3 @@ { | ||
"type": "module", | ||
"version": "0.1.7", | ||
"version": "0.1.8", | ||
"license": "MIT", | ||
@@ -8,0 +8,0 @@ "engines": { |
@@ -19,200 +19,242 @@ import { | ||
export function createBasicEncoder( | ||
api: ThreadEncodingStrategyApi, | ||
): ThreadEncodingStrategy { | ||
const functionsToId = new Map<AnyFunction, string>(); | ||
const idsToFunction = new Map<string, AnyFunction>(); | ||
const idsToProxy = new Map<string, AnyFunction>(); | ||
export function createBasicEncoderWithOverrides({ | ||
encode: encodeOverride, | ||
decode: decodeOverride, | ||
}: { | ||
encode?( | ||
value: unknown, | ||
api: ThreadEncodingStrategyApi & Pick<ThreadEncodingStrategy, 'encode'>, | ||
): ReturnType<ThreadEncodingStrategy['encode']> | undefined; | ||
decode?( | ||
value: unknown, | ||
retainedBy: Iterable<MemoryRetainer> | undefined, | ||
api: ThreadEncodingStrategyApi & Pick<ThreadEncodingStrategy, 'decode'>, | ||
): unknown; | ||
} = {}) { | ||
function createBasicEncoder( | ||
api: ThreadEncodingStrategyApi, | ||
): ThreadEncodingStrategy { | ||
const functionsToId = new Map<AnyFunction, string>(); | ||
const idsToFunction = new Map<string, AnyFunction>(); | ||
const idsToProxy = new Map<string, AnyFunction>(); | ||
return { | ||
encode, | ||
decode, | ||
call(id, args) { | ||
const stackFrame = new StackFrame(); | ||
const func = idsToFunction.get(id); | ||
const encodeOverrideApi = {...api, encode}; | ||
const decodeOverrideApi = {...api, decode}; | ||
if (func == null) { | ||
throw new Error( | ||
'You attempted to call a function that was already released.', | ||
); | ||
} | ||
return { | ||
encode, | ||
decode, | ||
call(id, args) { | ||
const stackFrame = new StackFrame(); | ||
const func = idsToFunction.get(id); | ||
const retainedBy = isMemoryManageable(func) | ||
? [stackFrame, ...func[RETAINED_BY]] | ||
: [stackFrame]; | ||
if (func == null) { | ||
throw new Error( | ||
'You attempted to call a function that was already released.', | ||
); | ||
} | ||
const result = func(...(decode(args, retainedBy) as any[])); | ||
const retainedBy = isMemoryManageable(func) | ||
? [stackFrame, ...func[RETAINED_BY]] | ||
: [stackFrame]; | ||
if (result == null || typeof result.then !== 'function') { | ||
stackFrame.release(); | ||
} | ||
const result = func(...(decode(args, retainedBy) as any[])); | ||
return (async () => { | ||
try { | ||
const resolved = await result; | ||
return resolved; | ||
} finally { | ||
if (result == null || typeof result.then !== 'function') { | ||
stackFrame.release(); | ||
} | ||
})(); | ||
}, | ||
release(id) { | ||
const func = idsToFunction.get(id); | ||
if (func) { | ||
idsToFunction.delete(id); | ||
functionsToId.delete(func); | ||
} | ||
}, | ||
terminate() { | ||
functionsToId.clear(); | ||
idsToFunction.clear(); | ||
idsToProxy.clear(); | ||
}, | ||
}; | ||
return (async () => { | ||
try { | ||
const resolved = await result; | ||
return resolved; | ||
} finally { | ||
stackFrame.release(); | ||
} | ||
})(); | ||
}, | ||
release(id) { | ||
const func = idsToFunction.get(id); | ||
function encode(value: unknown): [any, Transferable[]?] { | ||
if (typeof value === 'object') { | ||
if (value == null) { | ||
return [value]; | ||
} | ||
if (func) { | ||
idsToFunction.delete(id); | ||
functionsToId.delete(func); | ||
} | ||
}, | ||
terminate() { | ||
functionsToId.clear(); | ||
idsToFunction.clear(); | ||
idsToProxy.clear(); | ||
}, | ||
}; | ||
const transferables: Transferable[] = []; | ||
const encodeValue = (value: any) => { | ||
const [fieldValue, nestedTransferables = []] = encode(value); | ||
transferables.push(...nestedTransferables); | ||
return fieldValue; | ||
}; | ||
function encode( | ||
value: unknown, | ||
context: {encoded: WeakSet<any>} = {encoded: new WeakSet()}, | ||
): [any, Transferable[]?] { | ||
const override = encodeOverride?.(value, encodeOverrideApi); | ||
if (typeof (value as any)[ENCODE_METHOD] === 'function') { | ||
const result = (value as ThreadEncodable)[ENCODE_METHOD]({ | ||
encode: encodeValue, | ||
}); | ||
if (override) return override; | ||
return [result, transferables]; | ||
} | ||
if (typeof value === 'object') { | ||
if (value == null) { | ||
return [value]; | ||
} | ||
if (Array.isArray(value)) { | ||
const result = value.map((item) => encodeValue(item)); | ||
if (context.encoded.has(value)) { | ||
return [undefined]; | ||
} | ||
context.encoded.add(value); | ||
const transferables: Transferable[] = []; | ||
const encodeValue = (value: any) => { | ||
const [fieldValue, nestedTransferables = []] = encode(value, context); | ||
transferables.push(...nestedTransferables); | ||
return fieldValue; | ||
}; | ||
if (typeof (value as any)[ENCODE_METHOD] === 'function') { | ||
const result = (value as ThreadEncodable)[ENCODE_METHOD]({ | ||
encode: encodeValue, | ||
}); | ||
return [result, transferables]; | ||
} | ||
if (Array.isArray(value)) { | ||
const result = value.map((item) => encodeValue(item)); | ||
return [result, transferables]; | ||
} | ||
const result: Record<string, any> = {}; | ||
for (const key of Object.keys(value)) { | ||
result[key] = encodeValue((value as any)[key]); | ||
} | ||
if ( | ||
(Symbol.asyncIterator in value || Symbol.iterator in value) && | ||
typeof (value as any).next === 'function' | ||
) { | ||
result.next ??= encodeValue((value as any).next.bind(value)); | ||
result.return ??= encodeValue((value as any).return.bind(value)); | ||
result.throw ??= encodeValue((value as any).throw.bind(value)); | ||
result[ASYNC_ITERATOR] = true; | ||
} | ||
return [result, transferables]; | ||
} | ||
const result: Record<string, any> = {}; | ||
if (typeof value === 'function') { | ||
if (functionsToId.has(value)) { | ||
const id = functionsToId.get(value)!; | ||
return [{[FUNCTION]: id}]; | ||
} | ||
for (const key of Object.keys(value)) { | ||
result[key] = encodeValue((value as any)[key]); | ||
} | ||
const id = api.uuid(); | ||
if ( | ||
(Symbol.asyncIterator in value || Symbol.iterator in value) && | ||
typeof (value as any).next === 'function' | ||
) { | ||
result.next ??= encodeValue((value as any).next.bind(value)); | ||
result.return ??= encodeValue((value as any).return.bind(value)); | ||
result.throw ??= encodeValue((value as any).throw.bind(value)); | ||
result[ASYNC_ITERATOR] = true; | ||
functionsToId.set(value, id); | ||
idsToFunction.set(id, value); | ||
return [{[FUNCTION]: id}]; | ||
} | ||
return [result, transferables]; | ||
return [value]; | ||
} | ||
if (typeof value === 'function') { | ||
if (functionsToId.has(value)) { | ||
const id = functionsToId.get(value)!; | ||
return [{[FUNCTION]: id}]; | ||
} | ||
function decode( | ||
value: unknown, | ||
retainedBy?: Iterable<MemoryRetainer>, | ||
): any { | ||
const override = decodeOverride?.(value, retainedBy, decodeOverrideApi); | ||
const id = api.uuid(); | ||
if (override) return override; | ||
functionsToId.set(value, id); | ||
idsToFunction.set(id, value); | ||
if (typeof value === 'object') { | ||
if (value == null) { | ||
return value as any; | ||
} | ||
return [{[FUNCTION]: id}]; | ||
} | ||
if (Array.isArray(value)) { | ||
return value.map((value) => decode(value, retainedBy)); | ||
} | ||
return [value]; | ||
} | ||
if (FUNCTION in value) { | ||
const id = (value as {[FUNCTION]: string})[FUNCTION]; | ||
function decode(value: unknown, retainedBy?: Iterable<MemoryRetainer>): any { | ||
if (typeof value === 'object') { | ||
if (value == null) { | ||
return value as any; | ||
} | ||
if (idsToProxy.has(id)) { | ||
return idsToProxy.get(id)! as any; | ||
} | ||
if (Array.isArray(value)) { | ||
return value.map((value) => decode(value, retainedBy)); | ||
} | ||
let retainCount = 0; | ||
let released = false; | ||
if (FUNCTION in value) { | ||
const id = (value as {[FUNCTION]: string})[FUNCTION]; | ||
const release = () => { | ||
retainCount -= 1; | ||
if (idsToProxy.has(id)) { | ||
return idsToProxy.get(id)! as any; | ||
} | ||
if (retainCount === 0) { | ||
released = true; | ||
idsToProxy.delete(id); | ||
api.release(id); | ||
} | ||
}; | ||
let retainCount = 0; | ||
let released = false; | ||
const retain = () => { | ||
retainCount += 1; | ||
}; | ||
const release = () => { | ||
retainCount -= 1; | ||
const retainers = new Set(retainedBy); | ||
if (retainCount === 0) { | ||
released = true; | ||
idsToProxy.delete(id); | ||
api.release(id); | ||
} | ||
}; | ||
const proxy = (...args: any[]) => { | ||
if (released) { | ||
throw new Error( | ||
'You attempted to call a function that was already released.', | ||
); | ||
} | ||
const retain = () => { | ||
retainCount += 1; | ||
}; | ||
if (!idsToProxy.has(id)) { | ||
throw new Error( | ||
'You attempted to call a function that was already revoked.', | ||
); | ||
} | ||
const retainers = new Set(retainedBy); | ||
return api.call(id, args); | ||
}; | ||
const proxy = (...args: any[]) => { | ||
if (released) { | ||
throw new Error( | ||
'You attempted to call a function that was already released.', | ||
); | ||
} | ||
Object.defineProperties(proxy, { | ||
[RELEASE_METHOD]: {value: release, writable: false}, | ||
[RETAIN_METHOD]: {value: retain, writable: false}, | ||
[RETAINED_BY]: {value: retainers, writable: false}, | ||
}); | ||
if (!idsToProxy.has(id)) { | ||
throw new Error( | ||
'You attempted to call a function that was already revoked.', | ||
); | ||
for (const retainer of retainers) { | ||
retainer.add(proxy as any); | ||
} | ||
return api.call(id, args); | ||
}; | ||
idsToProxy.set(id, proxy); | ||
Object.defineProperties(proxy, { | ||
[RELEASE_METHOD]: {value: release, writable: false}, | ||
[RETAIN_METHOD]: {value: retain, writable: false}, | ||
[RETAINED_BY]: {value: retainers, writable: false}, | ||
}); | ||
for (const retainer of retainers) { | ||
retainer.add(proxy as any); | ||
return proxy as any; | ||
} | ||
idsToProxy.set(id, proxy); | ||
const result: Record<string | symbol, any> = {}; | ||
return proxy as any; | ||
} | ||
for (const key of Object.keys(value)) { | ||
if (key === ASYNC_ITERATOR) { | ||
result[Symbol.asyncIterator] = () => result; | ||
} else { | ||
result[key] = decode((value as any)[key], retainedBy); | ||
} | ||
} | ||
const result: Record<string | symbol, any> = {}; | ||
for (const key of Object.keys(value)) { | ||
if (key === ASYNC_ITERATOR) { | ||
result[Symbol.asyncIterator] = () => result; | ||
} else { | ||
result[key] = decode((value as any)[key], retainedBy); | ||
} | ||
return result; | ||
} | ||
return result; | ||
return value; | ||
} | ||
} | ||
return value; | ||
} | ||
return createBasicEncoder; | ||
} | ||
export const createBasicEncoder = createBasicEncoderWithOverrides(); |
@@ -1,1 +0,1 @@ | ||
export {createBasicEncoder} from './basic'; | ||
export {createBasicEncoder, createBasicEncoderWithOverrides} from './basic'; |
@@ -16,3 +16,3 @@ export {createThread} from './thread'; | ||
} from './targets'; | ||
export {createBasicEncoder} from './encoding'; | ||
export {createBasicEncoder, createBasicEncoderWithOverrides} from './encoding'; | ||
export {createThreadAbortSignal, acceptThreadAbortSignal} from './abort'; | ||
@@ -19,0 +19,0 @@ export type {ThreadAbortSignal} from './abort'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
140395
2446