liminal 0.5.11 → 0.5.13
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/CHANGELOG.md +14 -0
- package/Config.ts +13 -0
- package/Context.ts +24 -32
- package/Definition.ts +32 -0
- package/EventBase.ts +1 -1
- package/Handler.ts +3 -5
- package/L/L.ts +5 -2
- package/L/all.ts +35 -0
- package/L/assistant.ts +5 -6
- package/L/catch.ts +16 -18
- package/L/context.ts +12 -0
- package/L/continuation.ts +13 -0
- package/L/emit.ts +10 -8
- package/L/infer.ts +22 -27
- package/L/message.ts +5 -9
- package/L/model.ts +6 -8
- package/L/reflect.ts +12 -0
- package/L/run.ts +29 -0
- package/L/strand.ts +14 -95
- package/L/stream.ts +11 -24
- package/L/system.ts +13 -8
- package/L/user.ts +13 -8
- package/LEvent.ts +9 -17
- package/LiminalAssertionError.ts +19 -0
- package/Model.ts +5 -3
- package/ModelRegistry.ts +1 -7
- package/Rune.test.ts +5 -0
- package/Rune.ts +24 -12
- package/Schema.ts +185 -0
- package/Strand.ts +253 -0
- package/Tool.ts +8 -16
- package/TypeAdapter.ts +3 -0
- package/dist/Config.d.ts +12 -0
- package/dist/Config.js +2 -0
- package/dist/Config.js.map +1 -0
- package/dist/Context.d.ts +11 -10
- package/dist/Context.js +15 -26
- package/dist/Context.js.map +1 -1
- package/dist/Definition.d.ts +10 -0
- package/dist/Definition.js +18 -0
- package/dist/Definition.js.map +1 -0
- package/dist/EventBase.js +1 -1
- package/dist/EventBase.js.map +1 -1
- package/dist/Handler.d.ts +3 -4
- package/dist/Handler.js +1 -2
- package/dist/Handler.js.map +1 -1
- package/dist/L/L.d.ts +5 -2
- package/dist/L/L.js +5 -2
- package/dist/L/L.js.map +1 -1
- package/dist/L/all.d.ts +10 -0
- package/dist/L/all.js +20 -0
- package/dist/L/all.js.map +1 -0
- package/dist/L/assistant.d.ts +2 -2
- package/dist/L/assistant.js +4 -5
- package/dist/L/assistant.js.map +1 -1
- package/dist/L/catch.d.ts +3 -2
- package/dist/L/catch.js +12 -15
- package/dist/L/catch.js.map +1 -1
- package/dist/L/context.d.ts +2 -0
- package/dist/L/context.js +12 -0
- package/dist/L/context.js.map +1 -0
- package/dist/L/continuation.d.ts +3 -0
- package/dist/L/continuation.js +12 -0
- package/dist/L/continuation.js.map +1 -0
- package/dist/L/emit.d.ts +3 -5
- package/dist/L/emit.js +8 -3
- package/dist/L/emit.js.map +1 -1
- package/dist/L/infer.d.ts +2 -5
- package/dist/L/infer.js +21 -22
- package/dist/L/infer.js.map +1 -1
- package/dist/L/message.d.ts +2 -4
- package/dist/L/message.js +4 -6
- package/dist/L/message.js.map +1 -1
- package/dist/L/model.d.ts +2 -4
- package/dist/L/model.js +4 -5
- package/dist/L/model.js.map +1 -1
- package/dist/L/reflect.d.ts +4 -0
- package/dist/L/reflect.js +10 -0
- package/dist/L/reflect.js.map +1 -0
- package/dist/L/run.d.ts +15 -0
- package/dist/L/run.js +16 -0
- package/dist/L/run.js.map +1 -0
- package/dist/L/strand.d.ts +4 -25
- package/dist/L/strand.js +8 -65
- package/dist/L/strand.js.map +1 -1
- package/dist/L/stream.d.ts +2 -4
- package/dist/L/stream.js +8 -19
- package/dist/L/stream.js.map +1 -1
- package/dist/L/system.d.ts +2 -4
- package/dist/L/system.js +4 -3
- package/dist/L/system.js.map +1 -1
- package/dist/L/user.d.ts +2 -4
- package/dist/L/user.js +4 -3
- package/dist/L/user.js.map +1 -1
- package/dist/LEvent.d.ts +13 -41
- package/dist/LEvent.js +4 -15
- package/dist/LEvent.js.map +1 -1
- package/dist/LiminalAssertionError.d.ts +8 -0
- package/dist/LiminalAssertionError.js +20 -0
- package/dist/LiminalAssertionError.js.map +1 -0
- package/dist/Model.d.ts +4 -2
- package/dist/Model.js +1 -1
- package/dist/Model.js.map +1 -1
- package/dist/ModelRegistry.d.ts +0 -2
- package/dist/ModelRegistry.js +0 -2
- package/dist/ModelRegistry.js.map +1 -1
- package/dist/Rune.d.ts +19 -7
- package/dist/Rune.js +8 -4
- package/dist/Rune.js.map +1 -1
- package/dist/Rune.test.d.ts +1 -0
- package/dist/Rune.test.js +5 -0
- package/dist/Rune.test.js.map +1 -0
- package/dist/Schema.d.ts +46 -0
- package/dist/Schema.js +130 -0
- package/dist/Schema.js.map +1 -0
- package/dist/Strand.d.ts +57 -0
- package/dist/Strand.js +177 -0
- package/dist/Strand.js.map +1 -0
- package/dist/Tool.d.ts +6 -5
- package/dist/Tool.js +3 -4
- package/dist/Tool.js.map +1 -1
- package/dist/TypeAdapter.d.ts +1 -0
- package/dist/TypeAdapter.js +3 -0
- package/dist/TypeAdapter.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/util/EnsureNarrow.d.ts +1 -0
- package/dist/util/EnsureNarrow.js +2 -0
- package/dist/util/EnsureNarrow.js.map +1 -0
- package/dist/util/JSONValue.d.ts +8 -0
- package/dist/util/JSONValue.js +20 -0
- package/dist/util/JSONValue.js.map +1 -0
- package/dist/util/attachCustomInspect.d.ts +1 -0
- package/dist/util/attachCustomInspect.js +11 -0
- package/dist/util/attachCustomInspect.js.map +1 -0
- package/dist/util/isTemplateStringsArray.d.ts +1 -0
- package/dist/util/isTemplateStringsArray.js +4 -0
- package/dist/util/isTemplateStringsArray.js.map +1 -0
- package/errors.ts +12 -0
- package/index.ts +6 -4
- package/package.json +3 -13
- package/tsconfig.json +1 -5
- package/util/EnsureNarrow.ts +1 -0
- package/util/JSONValue.ts +20 -0
- package/util/attachCustomInspect.ts +14 -0
- package/util/isTemplateStringsArray.ts +3 -0
- package/Fiber.ts +0 -126
- package/L/_common.ts +0 -6
- package/L/rune.ts +0 -12
- package/MessageRegistry.ts +0 -22
- package/Runic.ts +0 -30
- package/ToolRegistry.ts +0 -10
- package/dist/Fiber.d.ts +0 -36
- package/dist/Fiber.js +0 -97
- package/dist/Fiber.js.map +0 -1
- package/dist/L/_common.d.ts +0 -4
- package/dist/L/_common.js +0 -7
- package/dist/L/_common.js.map +0 -1
- package/dist/L/rune.d.ts +0 -3
- package/dist/L/rune.js +0 -9
- package/dist/L/rune.js.map +0 -1
- package/dist/MessageRegistry.d.ts +0 -9
- package/dist/MessageRegistry.js +0 -15
- package/dist/MessageRegistry.js.map +0 -1
- package/dist/Runic.d.ts +0 -9
- package/dist/Runic.js +0 -18
- package/dist/Runic.js.map +0 -1
- package/dist/ToolRegistry.d.ts +0 -6
- package/dist/ToolRegistry.js +0 -8
- package/dist/ToolRegistry.js.map +0 -1
package/L/system.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { isTemplateStringsArray } from "liminal-util"
|
|
2
1
|
import type { LEvent } from "../LEvent.ts"
|
|
3
2
|
import type { Rune } from "../Rune.ts"
|
|
3
|
+
import { isTemplateStringsArray } from "../util/isTemplateStringsArray.ts"
|
|
4
4
|
import { message } from "./message.ts"
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export function system(
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
export function system(
|
|
7
|
+
template: TemplateStringsArray,
|
|
8
|
+
...substitutions: Array<number | string>
|
|
9
|
+
): Generator<Rune<LEvent>, void>
|
|
10
|
+
export function system(value: string): Generator<Rune<LEvent>, void>
|
|
11
|
+
export function system(
|
|
12
|
+
e0: TemplateStringsArray | string,
|
|
13
|
+
...rest: Array<number | string>
|
|
14
|
+
): Generator<Rune<LEvent>, void> {
|
|
15
|
+
return message("system", [{
|
|
16
|
+
part: isTemplateStringsArray(e0) ? String.raw(e0, ...rest) : e0,
|
|
17
|
+
}])
|
|
13
18
|
}
|
package/L/user.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { isTemplateStringsArray } from "liminal-util"
|
|
2
1
|
import type { LEvent } from "../LEvent.ts"
|
|
3
2
|
import type { Rune } from "../Rune.ts"
|
|
3
|
+
import { isTemplateStringsArray } from "../util/isTemplateStringsArray.ts"
|
|
4
4
|
import { message } from "./message.ts"
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export function user(
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
export function user(
|
|
7
|
+
template: TemplateStringsArray,
|
|
8
|
+
...substitutions: Array<number | string>
|
|
9
|
+
): Generator<Rune<LEvent>, void>
|
|
10
|
+
export function user(value: string): Generator<Rune<LEvent>, void>
|
|
11
|
+
export function user(
|
|
12
|
+
e0: TemplateStringsArray | string,
|
|
13
|
+
...rest: Array<number | string>
|
|
14
|
+
): Generator<Rune<LEvent>, void> {
|
|
15
|
+
return message("user", [{
|
|
16
|
+
part: isTemplateStringsArray(e0) ? String.raw(e0, ...rest) : e0,
|
|
17
|
+
}])
|
|
13
18
|
}
|
package/LEvent.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type { SchemaObject } from "liminal-schema"
|
|
2
1
|
import { EventBase } from "./EventBase.ts"
|
|
3
2
|
import type { Message } from "./Message.ts"
|
|
4
3
|
import type { Model } from "./Model.ts"
|
|
4
|
+
import type { Schema } from "./Schema.ts"
|
|
5
|
+
import type { StrandStatus } from "./Strand.ts"
|
|
5
6
|
|
|
6
7
|
export type LEvent =
|
|
7
|
-
|
|
|
8
|
-
| FiberResolved
|
|
9
|
-
| FiberStarted
|
|
8
|
+
| StrandStatusChanged
|
|
10
9
|
| InferenceRequested
|
|
11
10
|
| Inferred
|
|
12
11
|
| MessageAppended
|
|
@@ -28,10 +27,10 @@ export class ModelRegistered extends EventBase(LEventTag, "model_registered") {
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export class InferenceRequested extends EventBase(LEventTag, "inference_requested") {
|
|
31
|
-
declare schema?:
|
|
30
|
+
declare schema?: Schema
|
|
32
31
|
constructor(
|
|
33
|
-
readonly requestId:
|
|
34
|
-
schema?:
|
|
32
|
+
readonly requestId: string,
|
|
33
|
+
schema?: Schema | undefined,
|
|
35
34
|
) {
|
|
36
35
|
super()
|
|
37
36
|
if (schema) {
|
|
@@ -42,7 +41,7 @@ export class InferenceRequested extends EventBase(LEventTag, "inference_requeste
|
|
|
42
41
|
|
|
43
42
|
export class Inferred extends EventBase(LEventTag, "inferred") {
|
|
44
43
|
constructor(
|
|
45
|
-
readonly requestId:
|
|
44
|
+
readonly requestId: string,
|
|
46
45
|
readonly inference: string,
|
|
47
46
|
) {
|
|
48
47
|
super()
|
|
@@ -55,15 +54,8 @@ export class MessageAppended extends EventBase(LEventTag, "message_appended") {
|
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
export class
|
|
59
|
-
|
|
60
|
-
export class FiberResolved extends EventBase(LEventTag, "fiber_resolved") {
|
|
61
|
-
constructor(readonly value: any) {
|
|
62
|
-
super()
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
export class FiberRejected extends EventBase(LEventTag, "fiber_rejected") {
|
|
66
|
-
constructor(readonly reason: any) {
|
|
57
|
+
export class StrandStatusChanged extends EventBase(LEventTag, "strand_status_changed") {
|
|
58
|
+
constructor(readonly status: StrandStatus) {
|
|
67
59
|
super()
|
|
68
60
|
}
|
|
69
61
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class LiminalAssertionError extends Error {
|
|
2
|
+
override readonly name = "LiminalAssertionError"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export namespace LiminalAssertionError {
|
|
6
|
+
export function assert(expr: unknown, msg = ""): asserts expr {
|
|
7
|
+
if (!expr) {
|
|
8
|
+
throw new LiminalAssertionError(msg)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function unimplemented(): never {
|
|
13
|
+
throw new LiminalAssertionError()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function unreachable(): never {
|
|
17
|
+
throw new LiminalAssertionError()
|
|
18
|
+
}
|
|
19
|
+
}
|
package/Model.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { SchemaObject } from "liminal-schema"
|
|
2
|
-
import { attachCustomInspect } from "liminal-util"
|
|
3
1
|
import type { Message } from "./Message.ts"
|
|
2
|
+
import type { SchemaObject } from "./Schema.ts"
|
|
3
|
+
import type { Tool } from "./Tool.ts"
|
|
4
|
+
import { attachCustomInspect } from "./util/attachCustomInspect.ts"
|
|
4
5
|
|
|
5
6
|
export class Model {
|
|
6
7
|
constructor(
|
|
@@ -16,7 +17,8 @@ export class Model {
|
|
|
16
17
|
export interface Envelope {
|
|
17
18
|
messages: Array<Message>
|
|
18
19
|
schema?: SchemaObject | undefined
|
|
19
|
-
signal
|
|
20
|
+
signal: AbortSignal
|
|
21
|
+
tools?: Set<Tool> | undefined
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export interface SealedEnvelope {
|
package/ModelRegistry.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ContextPart } from "./Context.ts"
|
|
2
1
|
import type { Model } from "./Model.ts"
|
|
3
2
|
|
|
4
3
|
/** An intrusive list for storing `Model`s. */
|
|
@@ -40,7 +39,7 @@ export class ModelRegistry {
|
|
|
40
39
|
node.prev = node.next = undefined
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
clone()
|
|
42
|
+
clone() {
|
|
44
43
|
const instance = new ModelRegistry()
|
|
45
44
|
for (let node = this.head; node; node = node.next) {
|
|
46
45
|
instance.register(node.model)
|
|
@@ -54,8 +53,3 @@ export interface ModelRegistryNode {
|
|
|
54
53
|
model: Model
|
|
55
54
|
next?: ModelRegistryNode | undefined
|
|
56
55
|
}
|
|
57
|
-
|
|
58
|
-
export const ModelRegistryContext: ContextPart<ModelRegistry> = ContextPart(
|
|
59
|
-
(parent) => parent?.clone() ?? new ModelRegistry(),
|
|
60
|
-
"model_registry",
|
|
61
|
-
)
|
package/Rune.test.ts
ADDED
package/Rune.ts
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Context } from "./Context.ts"
|
|
2
|
+
import type { Definition } from "./Definition.ts"
|
|
2
3
|
|
|
3
|
-
export interface Rune<
|
|
4
|
-
E: E
|
|
5
|
-
(fiber: Fiber): any
|
|
4
|
+
export interface Rune<E> {
|
|
6
5
|
[RuneKey]: true
|
|
7
|
-
|
|
6
|
+
value: {
|
|
7
|
+
kind: "continuation"
|
|
8
|
+
debug: string
|
|
9
|
+
f: () => any
|
|
10
|
+
} | {
|
|
11
|
+
kind: "event"
|
|
12
|
+
event: E
|
|
13
|
+
} | {
|
|
14
|
+
kind: "reflect"
|
|
15
|
+
} | {
|
|
16
|
+
kind: "child"
|
|
17
|
+
definition: Definition
|
|
18
|
+
context?: Context | undefined
|
|
19
|
+
}
|
|
8
20
|
}
|
|
9
21
|
|
|
10
|
-
export
|
|
11
|
-
export type E<X extends Rune
|
|
22
|
+
export namespace Rune {
|
|
23
|
+
export type E<X extends Rune<any>> = X extends Rune<infer E> ? E : never
|
|
24
|
+
|
|
25
|
+
export function is(value: unknown): value is Rune<any> {
|
|
26
|
+
return typeof value === "object" && value !== null && RuneKey in value
|
|
27
|
+
}
|
|
12
28
|
}
|
|
13
29
|
|
|
14
|
-
export const RuneKey: unique symbol = Symbol.for("liminal/
|
|
30
|
+
export const RuneKey: unique symbol = Symbol.for("liminal/RuneKey")
|
|
15
31
|
export type RuneKey = typeof RuneKey
|
|
16
|
-
|
|
17
|
-
export function isRune(value: unknown): value is Rune {
|
|
18
|
-
return typeof value === "object" && value !== null && RuneKey in value
|
|
19
|
-
}
|
package/Schema.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { subtle } from "node:crypto"
|
|
2
|
+
import { LiminalAssertionError as LE } from "./LiminalAssertionError.ts"
|
|
3
|
+
|
|
4
|
+
export type Schema<T = any> = SchemaType<T> | SchemaAnyOf<T>
|
|
5
|
+
|
|
6
|
+
export type SchemaType<T = any> =
|
|
7
|
+
| SchemaNull<T>
|
|
8
|
+
| SchemaBoolean<T>
|
|
9
|
+
| SchemaInteger<T>
|
|
10
|
+
| SchemaNumber<T>
|
|
11
|
+
| SchemaString<T>
|
|
12
|
+
| SchemaArray<T>
|
|
13
|
+
| SchemaObject<T>
|
|
14
|
+
|
|
15
|
+
export interface SchemaNull<T = any> extends SchemaTypeBase<"null", T> {}
|
|
16
|
+
|
|
17
|
+
export interface SchemaBoolean<T = any> extends SchemaTypeBase<"boolean", T> {}
|
|
18
|
+
|
|
19
|
+
export interface SchemaInteger<T = any> extends SchemaTypeBase<"integer", T> {}
|
|
20
|
+
|
|
21
|
+
export interface SchemaNumber<T = any> extends SchemaTypeBase<"number", T> {}
|
|
22
|
+
|
|
23
|
+
export interface SchemaString<T = any> extends SchemaTypeBase<"string", T> {
|
|
24
|
+
enum?: Array<string>
|
|
25
|
+
const?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SchemaArray<T = any> extends SchemaTypeBase<"array", T> {
|
|
29
|
+
items: Schema
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SchemaObject<T = any> extends SchemaTypeBase<"object", T> {
|
|
33
|
+
properties: Record<string, Schema>
|
|
34
|
+
required: Array<string>
|
|
35
|
+
additionalProperties: false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface SchemaTypeBase<K extends SchemaTypeName, T> extends SchemaBase<T> {
|
|
39
|
+
type: K
|
|
40
|
+
anyOf?: never
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type SchemaTypeName = "null" | "boolean" | "integer" | "number" | "string" | "array" | "object"
|
|
44
|
+
|
|
45
|
+
export interface SchemaAnyOf<T = any> extends SchemaBase<T> {
|
|
46
|
+
type?: never
|
|
47
|
+
anyOf: Array<Schema>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SchemaBase<T> {
|
|
51
|
+
T: T
|
|
52
|
+
description?: string
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export namespace Schema {
|
|
56
|
+
const ids = new WeakMap<Schema, Record<string, string>>()
|
|
57
|
+
const schemas = new WeakMap<WeakKey, Schema>()
|
|
58
|
+
const validators = new WeakMap<Schema, (value: unknown) => Promise<unknown>>()
|
|
59
|
+
|
|
60
|
+
export function compile<T>(key: WeakKey, {
|
|
61
|
+
schema: schema_,
|
|
62
|
+
validate,
|
|
63
|
+
}: {
|
|
64
|
+
schema(): unknown
|
|
65
|
+
validate(value: unknown): Promise<T>
|
|
66
|
+
}) {
|
|
67
|
+
let schema = schemas.get(key)
|
|
68
|
+
if (!schema) {
|
|
69
|
+
schema = Schema.validate(schema_())
|
|
70
|
+
schemas.set(key, schema)
|
|
71
|
+
}
|
|
72
|
+
validators.set(schema, validate)
|
|
73
|
+
return schema
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function validateValue<T>(schema: Schema<T>, value: unknown): Promise<T> {
|
|
77
|
+
const validator = validators.get(schema)
|
|
78
|
+
LE.assert(validator)
|
|
79
|
+
return await validator(value) as never
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function id(schema: Schema, description?: string): Promise<string> {
|
|
83
|
+
description = description ?? ""
|
|
84
|
+
if (!ids.has(schema)) {
|
|
85
|
+
ids.set(schema, {})
|
|
86
|
+
}
|
|
87
|
+
const schemaIds = ids.get(schema)!
|
|
88
|
+
let id = schemaIds[description]
|
|
89
|
+
if (!id) {
|
|
90
|
+
const content = `${description}_${JSON.stringify(schema)}`
|
|
91
|
+
const bytes = new Uint8Array(await subtle.digest("SHA-256", new TextEncoder().encode(content)))
|
|
92
|
+
let binary = ""
|
|
93
|
+
for (const b of bytes) binary += String.fromCharCode(b)
|
|
94
|
+
id = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")
|
|
95
|
+
schemaIds[description] = id
|
|
96
|
+
}
|
|
97
|
+
return id
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function wrap(schema: Schema): SchemaObject {
|
|
101
|
+
return {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: { value: schema },
|
|
104
|
+
additionalProperties: false,
|
|
105
|
+
required: ["value"],
|
|
106
|
+
} satisfies Omit<SchemaObject, "T"> as never
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function validate(value: unknown): Schema {
|
|
110
|
+
LE.assert(typeof value === "object")
|
|
111
|
+
LE.assert(value !== null)
|
|
112
|
+
if ("anyOf" in value) {
|
|
113
|
+
LE.assert(Array.isArray(value.anyOf))
|
|
114
|
+
return {
|
|
115
|
+
anyOf: value.anyOf.map(validate),
|
|
116
|
+
} satisfies Omit<SchemaAnyOf, "T"> as never
|
|
117
|
+
} else {
|
|
118
|
+
if ("enum" in value) {
|
|
119
|
+
LE.assert(Array.isArray(value.enum))
|
|
120
|
+
LE.assert(!("const" in value))
|
|
121
|
+
value.enum.forEach((v) => LE.assert(typeof v === "string"))
|
|
122
|
+
return {
|
|
123
|
+
type: "string",
|
|
124
|
+
enum: value.enum,
|
|
125
|
+
} satisfies Omit<SchemaString, "T"> as never
|
|
126
|
+
} else if ("const" in value) {
|
|
127
|
+
LE.assert(typeof value.const === "string")
|
|
128
|
+
return {
|
|
129
|
+
type: "string",
|
|
130
|
+
const: value.const,
|
|
131
|
+
} satisfies Omit<SchemaString, "T"> as never
|
|
132
|
+
} else if ("type" in value) {
|
|
133
|
+
LE.assert("type" in value)
|
|
134
|
+
LE.assert(typeof value.type === "string")
|
|
135
|
+
LE.assert(SCHEMA_TYPE_NAMES[value.type])
|
|
136
|
+
switch (value.type) {
|
|
137
|
+
case "null":
|
|
138
|
+
case "boolean":
|
|
139
|
+
case "integer":
|
|
140
|
+
case "number": {
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
case "string": {
|
|
144
|
+
if ("const" in value) {
|
|
145
|
+
LE.assert(typeof value.const === "string")
|
|
146
|
+
}
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
case "array": {
|
|
150
|
+
LE.assert("items" in value)
|
|
151
|
+
return {
|
|
152
|
+
type: "array",
|
|
153
|
+
items: validate(value.items),
|
|
154
|
+
} satisfies Omit<SchemaArray, "T"> as never
|
|
155
|
+
}
|
|
156
|
+
case "object": {
|
|
157
|
+
LE.assert("properties" in value)
|
|
158
|
+
LE.assert(typeof value.properties === "object")
|
|
159
|
+
const { properties } = value
|
|
160
|
+
LE.assert(properties !== null)
|
|
161
|
+
return {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: Object.fromEntries(
|
|
164
|
+
Object.entries(properties).map(([key, value]) => [key, validate(value)]),
|
|
165
|
+
),
|
|
166
|
+
required: Object.keys(properties),
|
|
167
|
+
additionalProperties: false,
|
|
168
|
+
} satisfies Omit<SchemaObject, "T"> as never
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return value as Schema
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const SCHEMA_TYPE_NAMES: Record<string, boolean> = {
|
|
178
|
+
null: true,
|
|
179
|
+
boolean: true,
|
|
180
|
+
integer: true,
|
|
181
|
+
number: true,
|
|
182
|
+
string: true,
|
|
183
|
+
array: true,
|
|
184
|
+
object: true,
|
|
185
|
+
} satisfies Record<SchemaTypeName, true>
|
package/Strand.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { Context } from "./Context.ts"
|
|
2
|
+
import { Definition } from "./Definition.ts"
|
|
3
|
+
import { StrandRejectedError } from "./errors.ts"
|
|
4
|
+
import { continuation } from "./L/continuation.ts"
|
|
5
|
+
import { StrandStatusChanged } from "./LEvent.ts"
|
|
6
|
+
import type { Rune } from "./Rune.ts"
|
|
7
|
+
import { attachCustomInspect } from "./util/attachCustomInspect.ts"
|
|
8
|
+
|
|
9
|
+
export interface StrandConfig {
|
|
10
|
+
parent?: Strand | undefined
|
|
11
|
+
context?: Context | undefined
|
|
12
|
+
signal?: AbortSignal | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let nextIndex: number = 0
|
|
16
|
+
|
|
17
|
+
export class Strand<Y extends Rune<any> = Rune<any>, T = any> implements Iterable<Y, T>, PromiseLike<T> {
|
|
18
|
+
declare T: T
|
|
19
|
+
declare Y: Y
|
|
20
|
+
|
|
21
|
+
readonly #controller: AbortController = new AbortController()
|
|
22
|
+
readonly signal: AbortSignal = this.#controller.signal
|
|
23
|
+
#handle?: ((this: Strand, event: any) => void) | undefined
|
|
24
|
+
#definition: Definition<Y, T>
|
|
25
|
+
status: StrandStatus<T> = { type: "untouched" }
|
|
26
|
+
readonly index: number = nextIndex++
|
|
27
|
+
readonly depth: number
|
|
28
|
+
declare readonly parent?: Strand
|
|
29
|
+
readonly context: Context
|
|
30
|
+
|
|
31
|
+
constructor(definition: Definition<Y, T>, config: StrandConfig) {
|
|
32
|
+
this.#definition = definition
|
|
33
|
+
this.depth = (config?.parent?.depth ?? -1) + 1
|
|
34
|
+
const { parent, context, signal: configSignal } = config ?? {}
|
|
35
|
+
if (parent) {
|
|
36
|
+
this.parent = parent
|
|
37
|
+
this.#attachSignal(parent.signal, () => ({
|
|
38
|
+
type: "parent_aborted",
|
|
39
|
+
reason: parent.signal.reason,
|
|
40
|
+
}))
|
|
41
|
+
}
|
|
42
|
+
if (configSignal) {
|
|
43
|
+
this.#attachSignal(configSignal, () => ({
|
|
44
|
+
type: "config_signal_aborted",
|
|
45
|
+
reason: configSignal.reason,
|
|
46
|
+
}))
|
|
47
|
+
}
|
|
48
|
+
if (context) {
|
|
49
|
+
this.context = context
|
|
50
|
+
const { handler } = context
|
|
51
|
+
if (handler) {
|
|
52
|
+
this.#handle = (function(this: Strand, event: any) {
|
|
53
|
+
try {
|
|
54
|
+
handler.call(this, event)
|
|
55
|
+
} catch (exception) {
|
|
56
|
+
this.#setTerminalStatus({
|
|
57
|
+
type: "handler_exception_thrown",
|
|
58
|
+
exception,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}).bind(this)
|
|
62
|
+
this.#handle(new StrandStatusChanged(this.status))
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
this.context = Context()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#setStatus(status: StrandStatus<T>): void {
|
|
70
|
+
this.status = status
|
|
71
|
+
this.#handle?.(new StrandStatusChanged(this.status))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#setTerminalStatus = (status: StrandStatus.Rejected | StrandStatus.Resolved<T>): void => {
|
|
75
|
+
this.#setStatus(status)
|
|
76
|
+
this.#controller.abort(this.status)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#attachSignal(
|
|
80
|
+
signal: AbortSignal,
|
|
81
|
+
getStatus: () => StrandStatus.Rejected.ConfigSignalAborted | StrandStatus.Rejected.ParentAborted,
|
|
82
|
+
): void {
|
|
83
|
+
const f = () => {
|
|
84
|
+
this.#setTerminalStatus(getStatus())
|
|
85
|
+
}
|
|
86
|
+
if (signal.aborted) {
|
|
87
|
+
return f()
|
|
88
|
+
}
|
|
89
|
+
signal.addEventListener("abort", f, { once: true })
|
|
90
|
+
this.#controller.signal.addEventListener(
|
|
91
|
+
"abort",
|
|
92
|
+
() => {
|
|
93
|
+
signal.removeEventListener("abort", f)
|
|
94
|
+
},
|
|
95
|
+
{ once: true },
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
then<TResult1 = T, TResult2 = never>(
|
|
100
|
+
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
101
|
+
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
|
|
102
|
+
): PromiseLike<TResult1 | TResult2> {
|
|
103
|
+
if (this.status.type !== "untouched") {
|
|
104
|
+
return this.#replay(this.status).then(onfulfilled, onrejected)
|
|
105
|
+
}
|
|
106
|
+
let resolve: (value: T) => void = undefined!
|
|
107
|
+
let reject: (reason?: unknown) => void = undefined!
|
|
108
|
+
const promise = new Promise<T>((resolve_, reject_) => {
|
|
109
|
+
resolve = resolve_
|
|
110
|
+
reject = reject_
|
|
111
|
+
})
|
|
112
|
+
this.status = {
|
|
113
|
+
type: "pending",
|
|
114
|
+
promise,
|
|
115
|
+
resolve,
|
|
116
|
+
reject,
|
|
117
|
+
}
|
|
118
|
+
this.#handle?.(new StrandStatusChanged(this.status))
|
|
119
|
+
const iterator = Definition.unwrap(this.#definition)
|
|
120
|
+
let nextArg: unknown
|
|
121
|
+
queueMicrotask(async () => {
|
|
122
|
+
try {
|
|
123
|
+
let current = await iterator.next()
|
|
124
|
+
while (!current.done) {
|
|
125
|
+
const rune = current.value
|
|
126
|
+
const { value } = rune
|
|
127
|
+
switch (value.kind) {
|
|
128
|
+
case "reflect": {
|
|
129
|
+
nextArg = this
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
case "continuation": {
|
|
133
|
+
nextArg = await value.f()
|
|
134
|
+
break
|
|
135
|
+
}
|
|
136
|
+
case "event": {
|
|
137
|
+
this.#handle?.(value.event)
|
|
138
|
+
nextArg = undefined
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
case "child": {
|
|
142
|
+
nextArg = await new Strand(value.definition, {
|
|
143
|
+
parent: this,
|
|
144
|
+
context: value.context ?? this.context.clone(),
|
|
145
|
+
}).then()
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
switch (this.status.type) {
|
|
150
|
+
case "config_signal_aborted":
|
|
151
|
+
case "parent_aborted":
|
|
152
|
+
case "continuation_exception_thrown":
|
|
153
|
+
case "handler_exception_thrown": {
|
|
154
|
+
try {
|
|
155
|
+
await iterator.return?.(undefined)
|
|
156
|
+
} catch (exception) {}
|
|
157
|
+
return Promise.reject(new StrandRejectedError(this.status))
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
current = await iterator.next(nextArg)
|
|
161
|
+
}
|
|
162
|
+
const { value } = current
|
|
163
|
+
this.#setTerminalStatus({
|
|
164
|
+
type: "resolved",
|
|
165
|
+
value,
|
|
166
|
+
})
|
|
167
|
+
resolve(value)
|
|
168
|
+
} catch (exception) {
|
|
169
|
+
this.#setTerminalStatus({
|
|
170
|
+
type: "continuation_exception_thrown",
|
|
171
|
+
exception,
|
|
172
|
+
})
|
|
173
|
+
reject(exception)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
return promise.then(onfulfilled, onrejected)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
*[Symbol.iterator](): Generator<Y, T> {
|
|
180
|
+
return yield* continuation("run_strand", () => this.then()) as any
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#replay(status: Exclude<StrandStatus, StrandStatus.Untouched>): Promise<T> {
|
|
184
|
+
switch (status.type) {
|
|
185
|
+
case "config_signal_aborted":
|
|
186
|
+
case "parent_aborted":
|
|
187
|
+
case "continuation_exception_thrown":
|
|
188
|
+
case "handler_exception_thrown": {
|
|
189
|
+
return Promise.reject(new StrandRejectedError(status))
|
|
190
|
+
}
|
|
191
|
+
case "resolved": {
|
|
192
|
+
return Promise.resolve(status.value)
|
|
193
|
+
}
|
|
194
|
+
case "pending": {
|
|
195
|
+
return status.promise
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static {
|
|
201
|
+
attachCustomInspect(this, ({ index, parent }) => ({ index, ...parent && { parent } }))
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export type StrandStatus<T = any> =
|
|
206
|
+
| StrandStatus.Untouched
|
|
207
|
+
| StrandStatus.Pending<T>
|
|
208
|
+
| StrandStatus.Resolved<T>
|
|
209
|
+
| StrandStatus.Rejected
|
|
210
|
+
export namespace StrandStatus {
|
|
211
|
+
export interface Untouched {
|
|
212
|
+
type: "untouched"
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface Pending<T> {
|
|
216
|
+
type: "pending"
|
|
217
|
+
promise: Promise<T>
|
|
218
|
+
resolve: (value: T) => void
|
|
219
|
+
reject: (reason?: unknown) => void
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface Resolved<T> {
|
|
223
|
+
type: "resolved"
|
|
224
|
+
value: T
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type Rejected =
|
|
228
|
+
| Rejected.ConfigSignalAborted
|
|
229
|
+
| Rejected.ParentAborted
|
|
230
|
+
| Rejected.ContinuationExceptionThrown
|
|
231
|
+
| Rejected.HandlerExceptionThrown
|
|
232
|
+
// | Rejected.ModelError
|
|
233
|
+
// | Rejected.ValidationError
|
|
234
|
+
// | Rejected.Timeout
|
|
235
|
+
export namespace Rejected {
|
|
236
|
+
export interface ConfigSignalAborted {
|
|
237
|
+
type: "config_signal_aborted"
|
|
238
|
+
reason: unknown
|
|
239
|
+
}
|
|
240
|
+
export interface ParentAborted {
|
|
241
|
+
type: "parent_aborted"
|
|
242
|
+
reason: unknown
|
|
243
|
+
}
|
|
244
|
+
export interface ContinuationExceptionThrown {
|
|
245
|
+
type: "continuation_exception_thrown"
|
|
246
|
+
exception: unknown
|
|
247
|
+
}
|
|
248
|
+
export interface HandlerExceptionThrown {
|
|
249
|
+
type: "handler_exception_thrown"
|
|
250
|
+
exception: unknown
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|