feat: add Astro reference site and organize book contents
Reference site / build (push) Canceled after 0s

This commit is contained in:
2026-10-09 23:40:37 +09:00
parent 6086f3c5ef
commit 01c9498e6d
39 changed files with 7772 additions and 0 deletions
+701
View File
@@ -0,0 +1,701 @@
# TypeScript API Model
## Status
この文書は、WIP HostをTypeScriptで実装するための概念的なAPI modelを整理するdraftである。
このdraftで示すfunction、object shape、generic type、class、method signatureは説明用であり、具体的なTypeScript API syntaxとして確定しない。Package構成、schema builderの最終名称、ownership、async context、error type、bundler integrationも未決定である。
Protocol上の確定事項は[Core Model](../2-core-model.md)、[Object tree / Schema](../2.1-object-tree-schema.md)、[Operation and Interface](../2.2-operation-and-interface.md)、[Protocol](../3-protocol.md)を参照する。動的なObject projectionの運用要件は[Dynamic Worldspace Projection](dynamic-worldspace-projection.md)、対応するRust modelは[Rust API Model](rust-api-model.md)を参照する。
## 目的
TypeScript APIでは、次のmodelを自然に表現できるようにする。
1. Runtimeに残るdefinition objectでWIP Interfaceを宣言する。
2. 同じdefinition objectからInterface Descriptor、Value codec、validationを構築する。
3. Definition objectからOperation implementationのTypeScript型を推論する。
4. Classまたはplain objectが、Protocol Objectとして公開するmetadataと、必要に応じて0個以上のInterfaceのOperation implementationを提供する。
5. Objectを返す時点で、公開するInterfaceのordered setを明示的に選択する。
6. Object instanceをWorldspace全体について常駐させず、`observe`と`call_operation`の解決時にdata-drivenに生成できる。
7. Custom transpiler、TypeScript compiler plugin、decorator metadataをcore APIの前提にしない。
このmodelでは、Protocol上のObjectとInterfaceの関連付けは`Object.interfaces`だけで表す。Operation implementationへ到達するためのTypeScript / JavaScript内部codeはSDK / Host Runtimeの実装詳細であり、新しいProtocol conceptを導入しない。
## TypeScriptの型消去
TypeScriptの`interface`、`type`、generic type argumentは、通常のJavaScript outputには残らない。
```ts
interface FileOperations {
read(args: ReadArguments): Promise<Uint8Array>;
}
```
このdeclarationだけから、runtimeにInterface Descriptorを取得することはできない。
```ts
host.registerInterface(FileOperations); // runtime valueではない
```
そのため、TypeScript APIではruntime valueをsource of truthにする。
```ts
const FileInterface = wip.interface({
// runtime schema
});
```
このobjectは通常のJavaScript valueとしてtranspile後も残る。TypeScript型は、このruntime definitionから推論する。
## Conceptual model
### Type definition
Record、Enum、Union等もruntime definition objectとして宣言する。
```ts
const WriteResult = wip.union({
name: "WriteResult",
cases: {
written: wip.record({
bytesWritten: wip.integer(),
}),
rejected: wip.record({
reason: wip.string(),
}),
},
});
```
TypeScript型はdefinitionから推論する。
```ts
type WriteResult = wip.Infer<typeof WriteResult>;
```
`wip.Infer`の具体的な型実装と、Union DTOのTypeScript representationは未決定である。Runtime definitionと推論された型が、同じProtocol `TypeExpr`および`Value` semanticsを表すことが要件である。
### Interface definition
WIP Interfaceはruntime definition objectとして宣言する。
```ts
const FileInterface = wip.interface({
documentation: {
summary: "Operations available on a file",
},
types: {
WriteResult,
},
operations: {
read: wip.operation({
name: "read",
documentation: {
summary: "Reads bytes from the file",
},
parameters: {
offset: wip.optional(wip.integer()),
length: wip.optional(wip.integer()),
},
returns: wip.bytes(),
}),
write: wip.operation({
name: "write",
documentation: {
summary: "Replaces the file contents",
},
parameters: {
content: wip.bytes(),
},
returns: WriteResult,
}),
},
});
```
このsyntaxはconceptualである。Definition objectのproperty名、documentationの指定方法、Named typeの参照方法、`optional`の表現は未決定である。
Interface definitionは、次のlogical declarationに対応する。
- 一つのruntime definition objectを、一つのInterface Descriptorを構築するTypeScript側definitionとして扱う。
- JavaScript variable名`FileInterface`はlocal code上の識別子であり、Protocol `InterfaceDescriptor`のfieldや構造化されたInterface参照としてそのまま公開しない。
- 各Operation definitionが保持するstableなstringをOperation Declarationの`name`として扱う。
- `parameters`で宣言されたstableなstringを名前付きOperation parameterとして扱う。
- `returns`をOperation return typeとして扱う。
- Runtime type definitionをDescriptorの`TypeExpr`とProtocol `Value`へ対応付ける。
- DocumentationをInterface、Operation、parameter、return、type、field、caseへ指定できるようにする。
Descriptorのshapeはruntime definition objectによって決まり、各classまたはplain objectのimplementationによって変化しない。
Operation、parameter、type、field、caseのProtocol名はfinalized definition内のstableなstring dataとして保持する。JavaScript variable名、constructor名、functionの`.name`、prototype上のmethod列挙から推測しない。Hostはincoming Operation名をfinalized dispatch table上のinternal tokenまたはaccessorへ解決し、Clientから受け取った名前をそのままobject property lookupへ使用しない。Object-literal keyをauthoring sugarとして使う場合にproperty manglingからどう保護するかは未決定である。
Definition objectはHostへの登録時に一つのimmutableなartifactとしてfinalizeする。MutableなProtocol Interface参照を提供する場合は、同じ参照へ新しいvalidatorと新しいfinalized artifactを対応付け、同じ参照とvalidatorの組から異なるDescriptorを返してはならない。
### Operation implementation type
Interface definitionから、必要なOperation implementationのTypeScript型を推論する。
```ts
type FileOperations =
wip.Implementation<typeof FileInterface>;
```
Conceptualには、次のようなobject typeへ展開される。
```ts
type FileOperations = {
readonly read: (args: {
offset?: number;
length?: number;
}) => Promise<Uint8Array>;
readonly write: (args: {
content: Uint8Array;
}) => Promise<WriteResult>;
};
```
Function-valued propertyを基本形とし、`strictFunctionTypes`を含むstrictなcompiler settingsの下でparameterをcontravariantに検査できるようにする。通常のclass method parameterはTypeScriptのassignabilityでbivariantに扱われる場合があるため、method-shapedな`implements`だけを「すべてのschema-valid argumentを安全に受け取れる」ことの完全な証明としては扱わない。
Definition-awareなbuilderでimplementationをcontextual typingする形も考えられる。
```ts
const operations = FileInterface.implement({
read: async (args) => {
// argsはruntime definitionから推論される
},
write: async (args) => {
// ...
},
});
```
`implement`の採用は未決定である。どの形でもruntime validationとcallable checkをauthorityとし、TypeScript型はdeveloper feedbackとして扱う。
このfunction property shapeは説明用である。Operation parameterを一つのarguments objectとして渡すか、request contextを別parameterにするか、sync returnを許可するかは未決定である。
### Object implementation
Classは、その時点でWorldspaceへ投影するObjectをHost内部で表現できる。
```ts
class File implements WipObject, FileOperations {
readonly #path: string;
readonly #objectMetadata: WipObjectMetadata;
constructor(
path: string,
objectMetadata: WipObjectMetadata,
) {
this.#path = path;
this.#objectMetadata = objectMetadata;
}
metadata(): WipObjectMetadata {
return this.#objectMetadata;
}
readonly read: FileOperations["read"] = async (args) => {
return filesystem.read(this.#path, args);
};
readonly write: FileOperations["write"] = async (args) => {
return filesystem.write(this.#path, args.content);
};
}
```
`WipObject`と`WipObjectMetadata`の具体的なshapeは未決定である。概念的にはProtocol Objectのうち、publication時に選択するInterface集合以外の公開metadataを提供する。
```ts
interface WipObject {
metadata(): WipObjectMetadata;
}
interface WipObjectMetadata {
name: string;
description?: string;
ref?: string;
validator?: Uint8Array;
}
```
`WipObjectMetadata`へ次を含めない。これらをimplementing classのprivate fieldとして保持することは禁止しない。
- Absolute path。PathはObjectのfieldではなく、Worldspace resolutionのcontextである。
- Children。ChildrenはTree上の配置contextである。
- 固定されたInterface集合。InterfaceはObjectを公開する時点で追加する。
- Backend connection、file descriptor、credential等のHost-private state。
Classが保持するpathやbackend handle等をOperation implementationに使用してよいが、Object metadataまたはProtocol Objectへ自動的にserializeしない。
Classの使用は必須ではない。Structural typingを利用してplain objectも使用できる。
```ts
const file = {
metadata() {
return objectMetadata;
},
read: async (args) => {
return filesystem.read(path, args);
},
write: async (args) => {
return filesystem.write(path, args.content);
},
} satisfies WipObject & FileOperations;
```
### Object publication
ObjectをWorldspaceへ返す時点で、object instanceと公開する0個以上のInterface definitionを明示的に組み合わせる。Interfaceは追加順を保持するordered setであり、同じdefinitionまたは同じ登録済み参照を重複して追加しない。
```ts
const file = new File(path, objectMetadata);
const object = wip.publish(file)
.expose(FileInterface, file);
```
Publication APIはconceptualに、追加する各definitionと、そのImplementation型を満たすbindingを受け取る。
```ts
interface WipInterfaceDefinition<Implementation extends object> {
// runtime descriptor, codec, and dispatch artifact
}
interface PublishedObject<T extends WipObject> {
expose<Implementation extends object>(
definition: WipInterfaceDefinition<Implementation>,
implementation: Implementation,
): PublishedObject<T>;
}
```
実際のTypeScript generic表現は未決定である。このconstraintは、strictなcompiler settingsとfunction-valued property shapeを使用する場合に、追加する各Interfaceへbindした値が必要なOperation functionを提供するか検査する。Object instance自身をimplementationとして渡しても、Interface専用のadapter objectを渡してもよく、どのInterfaceも追加しないObjectも表現できる。通常のclass methodやtype assertion等で検査を弱められるため、各Interfaceを追加する時点とdispatch時のruntime callable check、およびValue validationは常に必要である。
Host Runtimeは各bindingを登録済みInterface参照と対応付ける。異なるInterfaceが同名Operationを宣言しても、選択されたInterface参照に対応するbindingだけをdispatch対象とし、単一object上の同名propertyへmergeしない。
`PublishedObject`はHost SDK内のconceptual typeであり、Protocol typeとして追加しない。Host Runtimeはこれを使用して次を構築する。
```ts
const protocolObject = {
name: metadata.name,
description: metadata.description,
interfaces: selectedInterfaceReferences,
ref: metadata.ref,
validator: metadata.validator,
};
```
Optional propertyの`undefined`はwireへ出力せず、field省略として扱う。`null`へ自動変換しない。
`selectedInterfaceReferences`は、Hostが各runtime Interface definitionを登録または公開するときに割り当てる構造化参照`{ scope, name }`を、publication APIへの追加順で並べたものである。同じ`(scope, name)`の重複は拒否し、Objectの配置pathに対する祖先制約も検証する。Variable名やJavaScript object identityをportableなProtocol参照としてそのまま使用することは要求しない。
`observe`はDescriptor自体をObjectへ埋め込まず、このInterface参照のordered setだけを返す。Clientが各参照を`fetch_interface`したとき、Host Runtimeはruntime definition objectから構築したDescriptorと、存在する場合はInterface validatorを返す。同じ参照とvalidatorの組のDescriptorはHost上で共有およびcacheできるため、Object instanceごとにDescriptorを再生成または再送する必要はない。
## Runtime definitionの責務
`wip.interface(...)`が返すdefinition objectは、conceptualには次を生成または提供する。
- Interface Descriptorを構築するruntime definition。
- Operation propertyとOperation Declarationの対応情報。
- ArgumentsのProtocol `Value` decoder。
- ResultのProtocol `Value` encoder。
- Argumentsとresultのruntime validation。
- Operation名から、そのInterfaceへbindしたimplementationの対応functionを呼び出すdispatch code。
- 各Interfaceの推論されたImplementation型を満たす値を、そのInterface参照へbindするtyped entry point。
- HostがInterface参照、optional validator、Descriptorを登録、取得、cacheするためのlocal identity。
TypeScriptのstructural type checkはcompile-timeのdeveloper feedbackである。Runtimeでは少なくとも次を検証する。
- Object metadataがProtocol上有効である。
- 追加する各InterfaceへbindしたOperation implementationが呼び出し可能である。
- 同じInterface参照をObjectへ重複して追加していない。
- ArgumentsがDescriptorに従っている。
- ResultがDescriptorに従っている。
- Integer、Number、Entry、Record、Union等の追加制約を満たす。
Generatedまたはgenericなdispatch codeはTypeScript SDK内部の実装詳細である。Host内部のdispatch mechanism、class名、prototype、constructor等をProtocolへ公開しない。
## TypeScript typeとProtocol type
Runtime definitionは、Descriptorの`TypeExpr`、Protocol `Value`、TypeScript static typeの三つを対応付ける。
Conceptualな対応例は次のとおりである。
| WIP definition | TypeScript representation | Runtime validation |
| --- | --- | --- |
| `wip.unit()` | 専用の`WipUnit` sentinel | Optional省略と区別する |
| `wip.boolean()` | `boolean` | Booleanのみ |
| `wip.integer()` | `number` | Safe integer範囲 |
| `wip.number()` | `number` | Finite binary64 |
| `wip.string()` | `string` | Unicode string |
| `wip.bytes()` | `Uint8Array`等 | Octet列 |
| `wip.json()` | JSON-compatible type | Bytesを拒否する |
| `wip.entry()` | Branded string等 | Canonical Worldspace path |
| `wip.record(...)` | Object type | Required、optional、unknown field |
| `wip.list(...)` | Arrayまたはreadonly array | 各itemを検証 |
| `wip.enum(...)` | String literal union | 宣言caseのみ |
| `wip.union(...)` | Discriminated union | Case、payload、shape |
この表の具体的なpublic typeは未決定である。
`wip.json()`が受け付けるのは、null、boolean、finite number、string、Array、およびstring-keyedなplain objectから再帰的に構成されるJSON-compatible valueだけである。`undefined`、`bigint`、`symbol`、function、`Uint8Array`、`Date`、`Map`、`Set`、class instance、cyclic object graph、NaN、正負Infinity、sparse array等を暗黙にJsonへ変換せず、validation failureとする。Integerとして表現する値にもProtocolの相互運用範囲を適用する。
TypeScriptの`number`はIntegerとNumberの両方に使用できるため、runtime schemaの位置に基づいて検証する。`wip.integer()`はProtocolの相互運用範囲を超える値を拒否する。`bigint`を暗黙にIntegerへ変換しない。
Protocol `Unit`は、例えば`wip.unitValue`のような専用singleton / sentinelで表す。`undefined`をUnitとして使用しない。RequiredなUnit fieldはsentinelを明示し、optionalなUnit fieldではsentinelがpresent Unit、property欠落またはoptional positionの`undefined`が省略を表す。
`wip.optional(T)`はfieldまたはparameterの省略を表し、`null`を許可する意味ではない。Implementationへ渡すarguments objectではproperty欠落を省略とし、optional positionへ明示された`undefined`をSDK convenienceとして省略へnormalizeしてよい。`undefined`自体をProtocol Valueとしてencodeせず、required positionの`undefined`はvalidation failureとする。
## Dynamic Worldspaceでのlifecycle
### `observe`
Projection implementationはrequest時にbackend dataを解決し、classまたはplain object instanceを生成する。
```ts
async function resolveFile(
path: WipPath,
subject: Subject,
): Promise<PublishedObject | undefined> {
const entry = await filesystem.resolve(path);
if (!entry || !canSee(subject, entry)) {
return undefined;
}
const file = new File(
entry.path,
createObjectMetadata(entry),
);
let object = wip.publish(file)
.expose(ReadOnlyFileInterface, {
read: file.read,
});
if (canWrite(subject, entry)) {
object = object.expose(WritableFileInterface, {
write: file.write,
});
}
return object;
}
```
Host Runtimeは`PublishedObject`からProtocol Objectを返す。Response生成後にこの`File` instanceを破棄してよく、Worldspace内の全fileに対応するinstanceを保持しない。
Requested depthが正の場合、Providerはdirect childrenをlazyに発見できる。Directory全体を先に収集してArrayとして返すことは要求しないが、一つのparentのobservationを成功させるには公開されたindexable childrenをclean exhaustionまで列挙しなければならない。ConceptualにはAsync Iterable等を利用できる。
```ts
const entries = filesystem.discoverChildren(path, budget);
for await (const entry of entries) {
const child = await projectCurrentObject(
entry.path,
context,
);
if (!child) {
continue;
}
observation.pushChild(child);
}
```
`discoverChildren`、`budget`、`observation`はconceptualであり、具体APIではない。必要なsemanticsは次である。
- 各childを現在の主体についてprojectし、非公開Objectとそのsubtreeを返さない。
- 公開されたindexable childrenをすべて列挙し、clean exhaustionで終了する。
- Backend failure、cancellation、resource budget超過をclean exhaustionとして扱わず、部分的なsuccess responseを返さない。
- Requested depthに達したnodeではchild discoveryを行わず、`children`を未観測として省略する。
- 異なるclassやplain objectを共通のpublished return typeとして扱える。
具体的なAsync Iterator contract、internal streaming、backpressure、error propagationは未決定である。
### `call_operation`
Hostはcall時に、requestのTargetと`InterfaceTarget`をHost Runtimeへ渡す。Runtimeは一つのatomic execution boundaryへ入ってから`target.path`を現在のObject implementationとstableなbackend対象へ解決する。
```ts
await runtime.callCurrent(
target,
interfaceTarget,
operation,
argumentsValue,
context,
(path, context) => resolveCurrentProjection(path, context),
);
```
`resolveCurrentProjection`と`runtime.callCurrent`というAPIを採用することは決定しない。ResolverはRuntimeが開始したexecution boundary内で呼ばれ、返す値はそのboundary中にObject identityとbackend対象を安定して保持するhandleまたはtransaction contextを含む必要がある。このexampleが示す要件は次である。
- 過去の`observe`で作成したobject instanceを保持していなくてもOperationを実行できる。
- Call時の現在の主体、Object visibility、Object metadata、Interface集合を同じProjection pathで再解決し、Objectが非公開なら`NotFound`を返す。
- Requestの`interface.reference`が現在の`Object.interfaces`に含まれ、self-scopeを含む祖先制約を満たすことを確認する。指定された`interface.scope_ref`と現在のscope Objectの`ref`も照合し、満たせない場合は`InterfaceMismatch`を返す。`scope_ref`の省略だけでは拒否しない。
- 現在のInterfaceにOperationが宣言されていなければ`OperationNotFound`、Operationを公開しつつ実行だけを拒否する場合は`PermissionDenied`を返す。
- `interface.validator`を現在のInterface validatorへ照合する。Mutable Interfaceにvalidatorがなければ`InterfaceValidatorRequired`、不一致なら`InterfaceValidatorMismatch`を返す。
- 現在のProjected Object、Object validator、選択されたInterfaceとそのvalidator、Descriptor、Operation、そのInterfaceへbindしたTypeScript implementation、実際のbackend対象を一つのexecution boundary内で一致させる。
- Object validatorが指定されている場合は、現在のObject identityと保護対象状態の両方へ照合し、同じexecution boundary内でOperationを実行する。
- Pathが別Objectを指す場合は、provider-local revisionや内容が同じでもObject validatorを一致させない。
- Providerのvalidatorがobject-localなrevisionだけの場合、HostはObject identityを含む形へnamespace化またはwrapできなければならない。
- Object validatorが省略され、Operationがvalidatorを要求する場合は、実行せず`ValidatorRequired`を返す。
- Object validatorなしの実行をOperationが許可する場合は、実行時にpathが指す現在Objectへ無条件に実行する。
- Runtime dispatchが、選択されたInterface definitionへbindしたOperation implementationを現在のObject contextへ適用する。異なるInterfaceの同名Operationをmergeまたは曖昧に解決しない。
- Operation実行後の現在状態に対応するvalidatorを、必要に応じて`CallOperationResponse`へ返せる。
Object validatorをObject identityへ結び付けるためにClient-facingな`ref`を要求しない。ProviderまたはHostはHost-privateなidentityを使用できるが、そのidentity confirmation、Object validator比較、Interface membershipとvalidator比較、DescriptorとInterface-specific implementation bindingの固定、authorization、Operation実行を一つのatomic boundaryで行う。
Operation実行後validatorを、同じobject instanceから再計算するか、Operation resultとともに返すか、backendを再観測するかは未決定である。どの方式でも、resultを生成した実行後状態とvalidatorを対応させなければならない。
Object instanceがpath文字列だけを保持し、validator比較後にpathを無条件で開き直す実装は、対象差し替えに対するatomicityを保証できない場合がある。TypeScript APIは、transaction、conditional update、backend handle、lock等を利用してbackend固有のatomicityを実現できる必要があるが、その抽象化は未決定である。
## 複数Interfaceの実装
一つのclassまたはplain objectは複数のInterface implementation typeを満たせる。Property名が衝突する場合やInterfaceごとに異なるbehaviorを提供する場合は、Interfaceごとのadapter objectをbindできる。
```ts
type ReadOnlyFileOperations =
wip.Implementation<typeof ReadOnlyFileInterface>;
type WritableFileOperations =
wip.Implementation<typeof WritableFileInterface>;
class File
implements
WipObject,
ReadOnlyFileOperations,
WritableFileOperations
{
// ...
}
```
Publication時に、現在の主体へ公開する0個以上のInterfaceを選択し、表示したい順で追加する。
```ts
let object = wip.publish(file)
.expose(ReadOnlyFileInterface, {
read: file.read,
});
if (authorization.canWrite) {
object = object.expose(WritableFileInterface, {
write: file.write,
});
}
return object;
```
Protocol Objectの`interfaces`は追加順を保持するordered setである。同じInterface参照の重複は拒否する。複数のImplementation bindingを一つのDescriptorやdispatch tableへ暗黙にmergeせず、各Interface Descriptorとbindingは独立したOperation集合およびbehaviorを提供する。
異なるInterfaceは同じOperation名を宣言できる。Dispatchはrequestの`InterfaceTarget.reference`とOperation名の組で一意に決まり、そのInterfaceへbindしたimplementationを使用するため、同じObjectに意味の異なる同名Operationを実装できる。
```ts
const object = wip.publish(file)
.expose(UserFileInterface, {
inspect: () => inspectForUser(file),
})
.expose(AuditFileInterface, {
inspect: () => inspectForAudit(file),
});
```
表示上のqualified表記は[Text Viewの参照表記](../4.2-text-view.md#参照表記)に従う。Signature全体をProtocol request fieldへparseし直さない。
Intersection type、interface extension、mixin等をDescriptorのcompositionへ自動変換しない。Protocol上のInterface inheritanceを暗黙に導入しない。
## HostへのInterface登録
Runtime Interface definitionをHostへ登録し、構造化されたInterface参照を割り当てる必要がある。
参照の絶対scopeとlocal name、および配置pathへの祖先制約は[2.2. Operation and Interface](../2.2-operation-and-interface.md#interfaceの識別とscope)に従う。以下のconceptual API例はscope / nameを渡す具体構文を省略しており、暗黙のroot scopeを定めるものではない。登録時のcontextと引数の具体APIは引き続き未決定である。Self-scopeは許可され、scopeはそのpathのObjectに紐づく。Scope Objectのoptionalな`ref`はProtocolの`scope_ref`として観測・検証し、`ref`がない場合のidentity追跡不能も許容する。Scopeの移動追跡や独立したscope validatorを導入しない。
Conceptualには、eager registrationとlazy registrationの両方が考えられる。
```ts
const readInterface = host.registerInterface(ReadOnlyFileInterface);
const writeInterface = host.registerInterface(WritableFileInterface);
const object = wip.publish(file)
.expose(readInterface, {
read: file.read,
})
.expose(writeInterface, {
write: file.write,
});
```
または、Hostが最初のpublication時に各definitionをinternしてよい。
どちらの場合も次を守る。
- MutableなInterface参照では`fetch_interface`がvalidatorを返し、同じ参照とvalidatorの組から異なるDescriptorを返さない。
- Validatorを返さないInterface参照は、その参照の有効期間中Descriptorをimmutableにする。
- Descriptorを更新する場合は同じ参照へ新しいvalidatorを発行するか、新しいInterface参照を使用する。
- Objectの`interfaces`へ設定した各Interface参照のDescriptorを、その主体から`fetch_interface`できる。
- 一つのObjectへ同じInterface参照を重複して追加しない。
- 構造的に同じDescriptorを同じ参照へdeduplicateすることは要求しない。
- Variable名、module path、JavaScript object address等を、そのままportableなProtocol semanticsとして扱わない。
- 一つのfinalized artifactを後からmutationしてDescriptorを変化させない。Hostはdefinitionをfreezeするか、検証済みのimmutable snapshotを保持できなければならない。
Registrationまたは最初のpublicationで、Descriptor、type codec、validator、Operation dispatch tableを一つのfinalized artifactとして再帰的に検証および固定する。`fetch_interface`、arguments / result validation、Operation dispatchはすべて同じartifactを使用する。Descriptorだけをsnapshotし、dispatch時にはmutation可能な元definitionを読み直す実装にはしない。Finalization後のmutationは拒否するか、既存artifactへ影響しないものとして扱う。
## Request contextとauthorization
Operation implementationは、Object instance以外にrequest contextを必要とし得る。
- 現在の主体。
- `AbortSignal`等のcancellation。
- Deadline。
- Tracing / audit context。
- Host serviceやbackend connectionへのaccess。
- Resource limit。
これらをmethod parameter、wrapper object、AsyncLocalStorage、closure等のどれで渡すかは未決定である。Protocol parameterとして宣言された値と、Hostが注入するcontextを明確に区別しなければならない。
Object publication時のInterface集合の選択と、Operation call時のmembership、Interface validator、authorizationの再評価を実装できる必要がある。以前の`observe`で公開されたInterface集合だけを根拠に、現在のOperation実行を許可してはならない。
Operation resultのRecord、List、Union等へnestedしたすべての`Entry`位置について、Hostは現在の主体にそのpathを認識させてよいことを再帰的に確認できなければならない。通常のstructural Value validationだけではこのvisibility checkを満たさない。Runtime definition、Projection、Host Runtimeのどこで確認するかは未決定である。
Operation argumentとして`Entry` pathを受け取った場合も、pathの所持をauthorizationの証明として扱わず、参照先へ作用する時点で現在の主体について認可する。
## Error model
TypeScript methodのerror表現は、次の四種類を区別できなければならない。
- Protocol validationまたはdispatch failure。
- Effectがcommitされていないと保証できるHost / backend implementation failure。
- Operationの未実行またはrollbackを保証できない`OperationOutcomeUnknown`。
- Interfaceがreturn typeとして宣言したdomain failure。
現在のProtocolでは、domain failureはOperation resultのUnion caseとして表す。Thrown value、rejected Promise、explicit result objectをdomain resultまたはHost errorへどう対応付けるかは、Interface authoring APIで明示しなければならない。
Arbitrary thrown value、Promise rejection、result encode failure、宣言型に違反するimplementation resultをdomain failureへ自動変換してはならない。Host Runtimeは、effectがcommitされていないと保証できる場合だけ`Internal`へmappingし、保証できない場合は`OperationOutcomeUnknown`へmappingする。Abortやtransport切断を未実行の証明として扱わず、自動retryしない。
## Custom transpilerを必須にしない
通常のTypeScript `interface`または`type`をsource of truthにしてInterface Descriptorを生成するには、TypeScript Compiler API、custom transformer、code generator等が必要になる。
Core APIはこれを要求しない。Runtime definition objectをsource of truthにすることで、通常の`tsc`、bundler、JavaScript runtimeで動作できるようにする。
Build-time toolingはoptionalなauthoring layerとして追加してよい。
```ts
/** @wip-interface */
interface FileOperations {
read(args: ReadArguments): Promise<Uint8Array>;
}
```
Toolingがこのdeclarationからruntime definition moduleを生成することはできるが、生成物はcore APIと同じruntime definition semanticsを満たさなければならない。
Build-time toolingには少なくとも次の課題がある。
- TypeScript compiler versionとCompiler APIへの依存。
- Bundler、incremental build、package boundaryとの統合。
- Generic、conditional type、mapped type、importされたtypeの解決。
- Documentation抽出。
- Sourceとgenerated runtime schemaの一致。
- Runtime validation codeの生成。
## Decoratorをcoreにしない
Decoratorだけでは、通常はUnion case、Record field、optional、Entry、Integer、nested type、generic、documentation等を完全なruntime schemaとして取得できない。
`emitDecoratorMetadata`等が提供するconstructor-level metadataだけでは、WIP Interface DescriptorとValue validationを再構築するには不足する。Decoratorをoptional sugarとして提供する場合も、明示的なruntime definitionまたはbuild-time generationへ接続する。
Class inheritanceをInterface declarationの基本形にすると、複数Interface implementation、plain object、data-drivenなpublicationが不必要に制限されるため、runtime definition objectをcore modelとする。
## 要件
TypeScript API modelは少なくとも次を満たす。
- Runtime definition objectをInterface Descriptorとruntime validationのsource of truthにする。
- 同じdefinitionからTypeScript implementation typeを推論できる。
- Strictなcompiler settingsとfunction-valued propertyを使用して、Object metadata providerが`WipObject`を満たし、各Interfaceへbindする値がそのImplementation型を満たすか検査できる。
- Compile-time型を完全なauthorityとせず、callable checkとProtocol boundaryのruntime validationを必須にする。
- 0個以上のInterfaceをpublication時に明示し、objectが持つpropertyやmethodの自動列挙に依存しない。
- Protocol上のOperation、parameter、type、field、case名をstableなstring dataとしてfinalizeし、constructor名やfunction `.name`から推測しない。
- Objectのpath、children、Interface集合をclassへ恒久的に固定しない。
- Classまたはplain object instanceをrequest scopeで生成および破棄できる。
- 全Object instanceの事前登録を要求しない。
- Direct childrenを現在の主体についてprojectし、非公開Objectとそのsubtreeを除外しつつlazyに発見し、success responseではclean exhaustionまで列挙する。
- Backend failure、cancellation、resource budget超過をclean exhaustionとして扱わず、部分的なobservationをsuccess responseとして返さない。
- 同じclassまたはplain objectへ複数のInterface-specific implementationを同時にbindでき、追加順を保持し、重複を拒否できる。
- 異なるInterfaceの同名Operationを、Interface参照とOperation名の組およびInterface-specific bindingでdispatchできる。
- Operation result内のnestedな`Entry`へ、現在の主体についてvisibility checkを適用できる。
- Host-private fieldをProtocol ObjectまたはValueへ暗黙にserializeしない。
- Call時の現在の主体、Object visibility、Object validator、Interface membershipとvalidator、Descriptor、Operation、Interface-specific implementation binding、backend対象を一貫したexecution boundaryで扱う。
- 非公開Object、Interface非membership、非公開Operation、公開済みOperationの実行拒否をそれぞれ`NotFound`、`InterfaceMismatch`、`OperationNotFound`、`PermissionDenied`へ対応付ける。
- Interface validatorの必須・不一致を`InterfaceValidatorRequired`、`InterfaceValidatorMismatch`へ対応付ける。
- Object validatorを現在のObject identityと保護対象状態へ結び付け、別Objectに同じprovider-local revisionがあっても一致させない。
- Object validator比較、identity confirmation、Interface membershipとvalidator比較、DescriptorとInterface-specific implementation bindingの固定、authorization、Operation実行を同じatomic boundaryで行う。
- Object validatorを要求するOperationへの省略を`ValidatorRequired`として拒否できる。
- Provider、Host Runtime、runtime definition codeのerrorを、effect未commitを保証できる`Internal`と、保証できない`OperationOutcomeUnknown`へ安全に対応付けられる。
- InvalidなObject、Descriptor、Operation resultをsuccess responseとして送信せず、Host-side validation failureをdomain failureへ変換しない。
- Custom transpiler、decorator、特定bundlerをHost実装の必須要件にしない。
- TypeScript SDK固有の型をProtocol semanticsとして要求しない。
## 非目標
このdraftは次を決定しない。
- 最終的なpackage、module、function、generic type、class、methodの名前。
- 特定のschema libraryまたはvalidation libraryの採用。
- 特定のJavaScript runtimeまたはbundler。
- Object instanceの常駐registryまたはactor model。
- JavaScript object、prototype、constructorをwireへ公開すること。
- TypeScript interface extensionをProtocol Interface inheritanceとして扱うこと。
- Backend entityとTypeScript objectまたはProtocol Objectの一対一対応。
- TypeScript Compiler APIまたはcustom transformerの必須化。
- Client SDKのtyped proxy API。
## 未決定事項
- `WipObject`と`WipObjectMetadata`の具体的なshape。
- Object metadata取得をsyncまたはasyncのどちらにするか。
- Publication APIがobjectを強参照、弱参照、closureのどれで保持するか。
- `wip.interface`、`wip.operation`、type builderの最終syntax。
- `wip.Infer`と`wip.Implementation`の具体的なgeneric type。
- `strictFunctionTypes`を含むminimum TypeScript compiler settings。
- Interface登録とscope / local name割当のlifecycle。
- 登録時にruntime definitionをdeep freezeするか、immutableなDescriptor / dispatch snapshotへ変換するか。
- Operation、parameter、type、field、caseのrename規則。
- Documentationのauthoring syntax。
- Runtime definitionと`TypeExpr`の対応。
- `readonly` property / arrayを許可するか。
- Class method、function property、closureのどれをOperation implementationとして許可するか。
- Runtime dispatchがmethod receiverの`this`を保持する方法。
- Operation property名とProtocol operation名をminifierやproperty manglingからどう保護するか。
- Sync returnを許可してPromiseへnormalizeするか。
- Request contextとProtocol parameterのAPI上の分離。
- Nestedな`Entry` resultのvisibility checkをRuntime definition、Projection、Host Runtimeのどこで行うか。
- Domain failure、thrown value、rejected Promise、Host errorの表現。
- Runtime dispatchとHost Runtimeの責務境界。
- Published ObjectをProviderから返す際のtype erasureまたは共通runtime representation。
- Validator比較とOperation実行を囲むbackend-specificなatomicity abstraction。
- Provider-local validatorをObject identityへnamespace化またはwrapするHost API。
- Operation実行後validatorをObject instance、Operation implementation、Providerのどこから取得するか。
- 主体ごとに異なるInterface combinationを追加したPublished Objectのstatic return typeをどう統一するか。
- TypeScript client APIを同じruntime definitionから生成するか。
- Build-time code generationをoptional packageとして提供するか。
これらを決定する際は、静的service objectだけでなく、Filesystem、database row、virtual aggregate、主体別Interface compositionを同じAPIで実装できるか評価する。