docs: rewrite reference and clarify interface abstraction
This commit is contained in:
@@ -2,15 +2,15 @@
|
||||
|
||||
## Status
|
||||
|
||||
この文書は、WIP HostをTypeScriptで実装するための概念的なAPI modelを整理するdraftである。
|
||||
TypeScriptの型は、通常のJavaScript outputには残らない。WIP Hostがruntimeに必要とするInterface Descriptorやvalidationをどう定義するか。このdraftは、TypeScriptでHostを実装するための概念的なAPI modelを提案する。
|
||||
|
||||
このdraftで示すfunction、object shape、generic type、class、method signatureは説明用であり、具体的なTypeScript API syntaxとして確定しない。Package構成、schema builderの最終名称、ownership、async context、error type、bundler integrationも未決定である。
|
||||
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)を参照する。
|
||||
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を自然に表現できるようにする。
|
||||
TypeScript APIでは、runtimeに残るdefinition objectを使い、Protocol上の宣言とTypeScript型の推論を対応付けるmodelを自然に表現できるようにする。Objectの実装と、ある時点で公開するInterfaceの選択は分けて扱う。
|
||||
|
||||
1. Runtimeに残るdefinition objectでWIP Interfaceを宣言する。
|
||||
2. 同じdefinition objectからInterface Descriptor、Value codec、validationを構築する。
|
||||
@@ -20,11 +20,11 @@ TypeScript APIでは、次のmodelを自然に表現できるようにする。
|
||||
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を導入しない。
|
||||
Protocol上でObjectとInterfaceを関連付けるのは`Object.interfaces`だけである。Operation implementationを呼び出すためのTypeScript / JavaScript内部codeはSDK / Host Runtimeの実装詳細として扱い、新しいProtocol conceptは導入しない。
|
||||
|
||||
## TypeScriptの型消去
|
||||
|
||||
TypeScriptの`interface`、`type`、generic type argumentは、通常のJavaScript outputには残らない。
|
||||
TypeScriptの`interface`、`type`、generic type argumentは、通常のJavaScript outputには残らない。たとえば、次のdeclarationだけからruntimeにInterface Descriptorを取得することはできない。
|
||||
|
||||
```ts
|
||||
interface FileOperations {
|
||||
@@ -32,13 +32,11 @@ interface FileOperations {
|
||||
}
|
||||
```
|
||||
|
||||
このdeclarationだけから、runtimeにInterface Descriptorを取得することはできない。
|
||||
|
||||
```ts
|
||||
host.registerInterface(FileOperations); // runtime valueではない
|
||||
```
|
||||
|
||||
そのため、TypeScript APIではruntime valueをsource of truthにする。
|
||||
そこで、TypeScript APIではruntime valueをsource of truthにする。
|
||||
|
||||
```ts
|
||||
const FileInterface = wip.interface({
|
||||
@@ -46,13 +44,13 @@ const FileInterface = wip.interface({
|
||||
});
|
||||
```
|
||||
|
||||
このobjectは通常のJavaScript valueとしてtranspile後も残る。TypeScript型は、このruntime definitionから推論する。
|
||||
このobjectは、通常のJavaScript valueとしてtranspile後も残る。TypeScript型は、このruntime definitionから推論する。
|
||||
|
||||
## Conceptual model
|
||||
|
||||
### Type definition
|
||||
|
||||
Record、Enum、Union等もruntime definition objectとして宣言する。
|
||||
Record、Enum、Union等の型も、runtime definition objectとして宣言する。
|
||||
|
||||
```ts
|
||||
const WriteResult = wip.union({
|
||||
@@ -70,17 +68,17 @@ const WriteResult = wip.union({
|
||||
});
|
||||
```
|
||||
|
||||
TypeScript型はdefinitionから推論する。
|
||||
このdefinitionから、TypeScript型を推論する。
|
||||
|
||||
```ts
|
||||
type WriteResult = wip.Infer<typeof WriteResult>;
|
||||
```
|
||||
|
||||
`wip.Infer`の具体的な型実装と、Union DTOのTypeScript representationは未決定である。Runtime definitionと推論された型が、同じProtocol `TypeExpr`および`Value` semanticsを表すことが要件である。
|
||||
`wip.Infer`の具体的な型実装とUnion DTOのTypeScript representationは、まだ決めていない。Runtime definitionと推論した型が、同じProtocol `TypeExpr`および`Value` semanticsを表すことを要件とする。
|
||||
|
||||
### Interface definition
|
||||
|
||||
WIP Interfaceはruntime definition objectとして宣言する。
|
||||
WIP Interfaceも、runtime definition objectとして宣言する。
|
||||
|
||||
```ts
|
||||
const FileInterface = wip.interface({
|
||||
@@ -125,9 +123,9 @@ const FileInterface = wip.interface({
|
||||
});
|
||||
```
|
||||
|
||||
このsyntaxはconceptualである。Definition objectのproperty名、documentationの指定方法、Named typeの参照方法、`optional`の表現は未決定である。
|
||||
このsyntaxはconceptualであり、具体的なAPIとして確定したものではない。Definition objectのproperty名、documentationの指定方法、Named typeの参照方法、`optional`の表現は未決定である。
|
||||
|
||||
Interface definitionは、次のlogical declarationに対応する。
|
||||
Interface definitionとProtocol上の宣言は、次のように対応付ける。
|
||||
|
||||
- 一つのruntime definition objectを、一つのInterface Descriptorを構築するTypeScript側definitionとして扱う。
|
||||
- JavaScript variable名`FileInterface`はlocal code上の識別子であり、Protocol `InterfaceDescriptor`のfieldや構造化されたInterface参照としてそのまま公開しない。
|
||||
@@ -137,22 +135,24 @@ Interface definitionは、次のlogical declarationに対応する。
|
||||
- Runtime type definitionをDescriptorの`TypeExpr`とProtocol `Value`へ対応付ける。
|
||||
- DocumentationをInterface、Operation、parameter、return、type、field、caseへ指定できるようにする。
|
||||
|
||||
Descriptorのshapeはruntime definition objectによって決まり、各classまたはplain objectのimplementationによって変化しない。
|
||||
Descriptorのshapeを決めるのはruntime definition objectである。各classまたはplain objectがどう実装するかによって、Descriptorは変化しない。
|
||||
|
||||
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からどう保護するかは未決定である。
|
||||
Operation、parameter、type、field、caseのProtocol名は、finalized definition内にstableなstring dataとして保持する。JavaScript variable名、constructor名、functionの`.name`、prototype上のmethod列挙から推測することはしない。
|
||||
|
||||
Definition objectはHostへの登録時に一つのimmutableなartifactとしてfinalizeする。MutableなProtocol Interface参照を提供する場合は、同じ参照へ新しいvalidatorと新しいfinalized artifactを対応付け、同じ参照とvalidatorの組から異なるDescriptorを返してはならない。
|
||||
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型を推論する。
|
||||
必要なOperation implementationのTypeScript型は、Interface definitionから推論する。
|
||||
|
||||
```ts
|
||||
type FileOperations =
|
||||
wip.Implementation<typeof FileInterface>;
|
||||
```
|
||||
|
||||
Conceptualには、次のようなobject typeへ展開される。
|
||||
概念的には、次のようなobject typeへ展開される。
|
||||
|
||||
```ts
|
||||
type FileOperations = {
|
||||
@@ -167,9 +167,9 @@ type FileOperations = {
|
||||
};
|
||||
```
|
||||
|
||||
Function-valued propertyを基本形とし、`strictFunctionTypes`を含むstrictなcompiler settingsの下でparameterをcontravariantに検査できるようにする。通常のclass method parameterはTypeScriptのassignabilityでbivariantに扱われる場合があるため、method-shapedな`implements`だけを「すべてのschema-valid argumentを安全に受け取れる」ことの完全な証明としては扱わない。
|
||||
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する形も考えられる。
|
||||
Definition-awareなbuilderによって、implementationをcontextual typingする形も考えられる。
|
||||
|
||||
```ts
|
||||
const operations = FileInterface.implement({
|
||||
@@ -183,13 +183,13 @@ const operations = FileInterface.implement({
|
||||
});
|
||||
```
|
||||
|
||||
`implement`の採用は未決定である。どの形でもruntime validationとcallable checkをauthorityとし、TypeScript型はdeveloper feedbackとして扱う。
|
||||
`implement`を採用するかは未決定である。どの形でも、実行時の判定はruntime validationとcallable checkをauthorityとし、TypeScript型はdeveloper feedbackとして扱う。
|
||||
|
||||
このfunction property shapeは説明用である。Operation parameterを一つのarguments objectとして渡すか、request contextを別parameterにするか、sync returnを許可するかは未決定である。
|
||||
上のfunction property shapeも説明用である。Operation parameterを一つのarguments objectとして渡すか、request contextを別parameterにするか、sync returnを許可するかは、まだ決めていない。
|
||||
|
||||
### Object implementation
|
||||
|
||||
Classは、その時点でWorldspaceへ投影するObjectをHost内部で表現できる。
|
||||
その時点でWorldspaceへ投影するObjectは、Host内部ではclassとして表現できる。
|
||||
|
||||
```ts
|
||||
class File implements WipObject, FileOperations {
|
||||
@@ -218,7 +218,7 @@ class File implements WipObject, FileOperations {
|
||||
}
|
||||
```
|
||||
|
||||
`WipObject`と`WipObjectMetadata`の具体的なshapeは未決定である。概念的にはProtocol Objectのうち、publication時に選択するInterface集合以外の公開metadataを提供する。
|
||||
`WipObject`と`WipObjectMetadata`は、Protocol Objectの公開metadataのうち、publication時に選ぶInterface集合以外を提供するための概念的な型である。具体的なshapeは未決定である。
|
||||
|
||||
```ts
|
||||
interface WipObject {
|
||||
@@ -233,16 +233,16 @@ interface WipObjectMetadata {
|
||||
}
|
||||
```
|
||||
|
||||
`WipObjectMetadata`へ次を含めない。これらをimplementing classのprivate fieldとして保持することは禁止しない。
|
||||
Classが保持する情報のすべてを公開metadataにするわけではない。次の情報は`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のpathやbackend handleはOperation implementationで使用できる。ただし、Object metadataやProtocol Objectへ自動的にserializeする対象にはしない。
|
||||
|
||||
Classの使用は必須ではない。Structural typingを利用してplain objectも使用できる。
|
||||
また、classの使用は必須ではない。Structural typingを利用し、plain objectを使うこともできる。
|
||||
|
||||
```ts
|
||||
const file = {
|
||||
@@ -262,7 +262,7 @@ const file = {
|
||||
|
||||
### Object publication
|
||||
|
||||
ObjectをWorldspaceへ返す時点で、object instanceと公開する0個以上のInterface definitionを明示的に組み合わせる。Interfaceは追加順を保持するordered setであり、同じdefinitionまたは同じ登録済み参照を重複して追加しない。
|
||||
ObjectをWorldspaceへ返す時点で、object instanceと、公開する0個以上のInterface definitionを明示的に組み合わせる。Interfaceは追加順を保持するordered setとし、同じdefinitionまたは同じ登録済み参照の重複追加は認めない。
|
||||
|
||||
```ts
|
||||
const file = new File(path, objectMetadata);
|
||||
@@ -270,7 +270,7 @@ const object = wip.publish(file)
|
||||
.expose(FileInterface, file);
|
||||
```
|
||||
|
||||
Publication APIはconceptualに、追加する各definitionと、そのImplementation型を満たすbindingを受け取る。
|
||||
Publication APIは、概念的には各definitionと、そのImplementation型を満たすbindingを受け取る。
|
||||
|
||||
```ts
|
||||
interface WipInterfaceDefinition<Implementation extends object> {
|
||||
@@ -285,11 +285,13 @@ interface PublishedObject<T extends WipObject> {
|
||||
}
|
||||
```
|
||||
|
||||
実際の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は常に必要である。
|
||||
実際のTypeScript generic表現は未決定である。このconstraintは、strictなcompiler settingsとfunction-valued property shapeを使用した場合に、各Interfaceへbindする値が必要なOperation functionを提供するかを検査するためのものである。
|
||||
|
||||
Host Runtimeは各bindingを登録済みInterface参照と対応付ける。異なるInterfaceが同名Operationを宣言しても、選択されたInterface参照に対応するbindingだけをdispatch対象とし、単一object上の同名propertyへmergeしない。
|
||||
ImplementationにはObject instance自身を渡しても、Interface専用のadapter objectを渡してもよい。Interfaceを一つも追加しないObjectも表現できる。ただし、通常のclass methodやtype assertion等で型の検査を弱められるため、各Interfaceの追加時とdispatch時にはruntime callable checkを行い、Value validationも常に行う必要がある。
|
||||
|
||||
`PublishedObject`はHost SDK内のconceptual typeであり、Protocol typeとして追加しない。Host Runtimeはこれを使用して次を構築する。
|
||||
Host Runtimeは、各bindingを登録済みInterface参照と対応付ける。異なるInterfaceが同名Operationを宣言していても、dispatch対象は選択されたInterface参照に対応するbindingだけであり、単一object上の同名propertyへmergeしない。
|
||||
|
||||
`PublishedObject`はHost SDK内のconceptual typeであり、Protocol typeには追加しない。Host Runtimeはこの値からProtocol Objectを構築する。
|
||||
|
||||
```ts
|
||||
const protocolObject = {
|
||||
@@ -301,15 +303,17 @@ const protocolObject = {
|
||||
};
|
||||
```
|
||||
|
||||
Optional propertyの`undefined`はwireへ出力せず、field省略として扱う。`null`へ自動変換しない。
|
||||
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参照としてそのまま使用することは要求しない。
|
||||
`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を再生成または再送する必要はない。
|
||||
`observe`でObjectに含めるのは、Descriptor自体ではなく、この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には次を生成または提供する。
|
||||
`wip.interface(...)`が返すdefinition objectには、Descriptorの構築に加えて、Valueの変換・検証とInterfaceごとのdispatchに必要な情報を提供する役割を想定する。
|
||||
|
||||
- Interface Descriptorを構築するruntime definition。
|
||||
- Operation propertyとOperation Declarationの対応情報。
|
||||
@@ -320,7 +324,7 @@ Optional propertyの`undefined`はwireへ出力せず、field省略として扱
|
||||
- 各Interfaceの推論されたImplementation型を満たす値を、そのInterface参照へbindするtyped entry point。
|
||||
- HostがInterface参照、optional validator、Descriptorを登録、取得、cacheするためのlocal identity。
|
||||
|
||||
TypeScriptのstructural type checkはcompile-timeのdeveloper feedbackである。Runtimeでは少なくとも次を検証する。
|
||||
TypeScriptのstructural type checkはcompile-timeのdeveloper feedbackである。Runtimeでは、少なくとも次を検証する必要がある。
|
||||
|
||||
- Object metadataがProtocol上有効である。
|
||||
- 追加する各InterfaceへbindしたOperation implementationが呼び出し可能である。
|
||||
@@ -329,13 +333,11 @@ TypeScriptのstructural type checkはcompile-timeのdeveloper feedbackである
|
||||
- ResultがDescriptorに従っている。
|
||||
- Integer、Number、Entry、Record、Union等の追加制約を満たす。
|
||||
|
||||
Generatedまたはgenericなdispatch codeはTypeScript SDK内部の実装詳細である。Host内部のdispatch mechanism、class名、prototype、constructor等をProtocolへ公開しない。
|
||||
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な対応例は次のとおりである。
|
||||
Runtime definitionは、Descriptorの`TypeExpr`、Protocol `Value`、TypeScript static typeの三つを対応付ける。次の表はconceptualな対応例であり、具体的なpublic typeは未決定である。
|
||||
|
||||
| WIP definition | TypeScript representation | Runtime validation |
|
||||
| --- | --- | --- |
|
||||
@@ -352,21 +354,19 @@ Conceptualな対応例は次のとおりである。
|
||||
| `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の相互運用範囲を適用する。
|
||||
|
||||
`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へ変換しない。
|
||||
|
||||
TypeScriptの`number`はIntegerとNumberの両方に使用できるため、runtime schemaの位置に基づいて検証する。`wip.integer()`はProtocolの相互運用範囲を超える値を拒否する。`bigint`を暗黙にIntegerへ変換しない。
|
||||
Protocol `Unit`には、たとえば`wip.unitValue`のような専用singleton / sentinelを使い、`undefined`は使わない。RequiredなUnit fieldではsentinelを明示する。OptionalなUnit fieldでは、sentinelがpresent Unitを表し、property欠落またはoptional positionの`undefined`が省略を表す。
|
||||
|
||||
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とする。
|
||||
同様に、`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を生成する。
|
||||
Projection implementationはrequest時にbackend dataを解決し、その時点のclassまたはplain object instanceを生成する。
|
||||
|
||||
```ts
|
||||
async function resolveFile(
|
||||
@@ -399,9 +399,9 @@ async function resolveFile(
|
||||
}
|
||||
```
|
||||
|
||||
Host Runtimeは`PublishedObject`からProtocol Objectを返す。Response生成後にこの`File` instanceを破棄してよく、Worldspace内の全fileに対応するinstanceを保持しない。
|
||||
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等を利用できる。
|
||||
Requested depthが正の場合、Providerはdirect childrenをlazyに発見できる。Directory全体を先に収集し、Arrayとして返すことは要求しない。ただし、一つのparentのobservationを成功させるには、公開されたindexable childrenをclean exhaustionまで列挙する必要がある。概念的には、Async Iterable等を利用できる。
|
||||
|
||||
```ts
|
||||
const entries = filesystem.discoverChildren(path, budget);
|
||||
@@ -420,7 +420,7 @@ for await (const entry of entries) {
|
||||
}
|
||||
```
|
||||
|
||||
`discoverChildren`、`budget`、`observation`はconceptualであり、具体APIではない。必要なsemanticsは次である。
|
||||
`discoverChildren`、`budget`、`observation`は具体APIではなく、conceptualな表記である。Childの発見と公開には、次のsemanticsを要求する。
|
||||
|
||||
- 各childを現在の主体についてprojectし、非公開Objectとそのsubtreeを返さない。
|
||||
- 公開されたindexable childrenをすべて列挙し、clean exhaustionで終了する。
|
||||
@@ -432,7 +432,7 @@ for await (const entry of entries) {
|
||||
|
||||
### `call_operation`
|
||||
|
||||
Hostはcall時に、requestのTargetと`InterfaceTarget`をHost Runtimeへ渡す。Runtimeは一つのatomic execution boundaryへ入ってから`target.path`を現在のObject implementationとstableなbackend対象へ解決する。
|
||||
Call時には現在のObjectを解決し直す。HostはrequestのTargetと`InterfaceTarget`をHost Runtimeへ渡し、Runtimeは一つのatomic execution boundaryに入ってから、`target.path`を現在のObject implementationとstableなbackend対象へ解決する。
|
||||
|
||||
```ts
|
||||
await runtime.callCurrent(
|
||||
@@ -445,7 +445,7 @@ await runtime.callCurrent(
|
||||
);
|
||||
```
|
||||
|
||||
`resolveCurrentProjection`と`runtime.callCurrent`というAPIを採用することは決定しない。ResolverはRuntimeが開始したexecution boundary内で呼ばれ、返す値はそのboundary中にObject identityとbackend対象を安定して保持するhandleまたはtransaction contextを含む必要がある。このexampleが示す要件は次である。
|
||||
`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`を返す。
|
||||
@@ -461,15 +461,15 @@ await runtime.callCurrent(
|
||||
- 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で行う。
|
||||
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を対応させなければならない。
|
||||
実行後のvalidatorをどこから取得するかも未決定である。同じobject instanceから再計算する方式、Operation resultとともに返す方式、backendを再観測する方式が考えられるが、いずれもresultを生成した実行後状態とvalidatorを対応させる必要がある。
|
||||
|
||||
Object instanceがpath文字列だけを保持し、validator比較後にpathを無条件で開き直す実装は、対象差し替えに対するatomicityを保証できない場合がある。TypeScript APIは、transaction、conditional update、backend handle、lock等を利用してbackend固有のatomicityを実現できる必要があるが、その抽象化は未決定である。
|
||||
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できる。
|
||||
一つのclassまたはplain objectは、複数のInterface implementation typeを満たせる。Property名が衝突する場合や、Interfaceごとに異なるbehaviorを提供する場合は、Interfaceごとのadapter objectをbindできる。
|
||||
|
||||
```ts
|
||||
type ReadOnlyFileOperations =
|
||||
@@ -488,7 +488,7 @@ class File
|
||||
}
|
||||
```
|
||||
|
||||
Publication時に、現在の主体へ公開する0個以上のInterfaceを選択し、表示したい順で追加する。
|
||||
現在の主体へ公開する0個以上のInterfaceはpublication時に選び、表示したい順に追加する。
|
||||
|
||||
```ts
|
||||
let object = wip.publish(file)
|
||||
@@ -505,9 +505,9 @@ if (authorization.canWrite) {
|
||||
return object;
|
||||
```
|
||||
|
||||
Protocol Objectの`interfaces`は追加順を保持するordered setである。同じInterface参照の重複は拒否する。複数のImplementation bindingを一つのDescriptorやdispatch tableへ暗黙にmergeせず、各Interface Descriptorとbindingは独立したOperation集合およびbehaviorを提供する。
|
||||
Protocol Objectの`interfaces`は追加順を保持するordered setであり、同じInterface参照の重複は拒否する。各Interface Descriptorとbindingは、それぞれ独立したOperation集合およびbehaviorを提供する。複数のImplementation bindingを一つのDescriptorやdispatch tableへ暗黙にmergeすることはしない。
|
||||
|
||||
異なるInterfaceは同じOperation名を宣言できる。Dispatchはrequestの`InterfaceTarget.reference`とOperation名の組で一意に決まり、そのInterfaceへbindしたimplementationを使用するため、同じObjectに意味の異なる同名Operationを実装できる。
|
||||
異なるInterfaceが同じOperation名を宣言しても、dispatchはrequestの`InterfaceTarget.reference`とOperation名の組で一意に決まる。そのInterfaceへbindしたimplementationを使うため、同じObjectに意味の異なる同名Operationを実装できる。
|
||||
|
||||
```ts
|
||||
const object = wip.publish(file)
|
||||
@@ -519,17 +519,17 @@ const object = wip.publish(file)
|
||||
});
|
||||
```
|
||||
|
||||
表示上のqualified表記は[Text Viewの参照表記](../4.2-text-view.md#参照表記)に従う。Signature全体をProtocol request fieldへparseし直さない。
|
||||
表示上のqualified表記は[Text Viewの参照表記](../4.2-text-view.md#参照表記)に従い、signature全体をProtocol request fieldへparseし直さない。
|
||||
|
||||
Intersection type、interface extension、mixin等をDescriptorのcompositionへ自動変換しない。Protocol上のInterface inheritanceを暗黙に導入しない。
|
||||
Intersection type、interface extension、mixin等をDescriptorのcompositionへ自動変換することはしない。これらによって、Protocol上のInterface inheritanceを暗黙に導入することもない。
|
||||
|
||||
## HostへのInterface登録
|
||||
|
||||
Runtime Interface definitionをHostへ登録し、構造化されたInterface参照を割り当てる必要がある。
|
||||
Runtime Interface definitionには、Hostへの登録を通じて構造化されたInterface参照を割り当てる必要がある。参照の絶対scopeとlocal name、および配置pathへの祖先制約は[2.2. Operation and Interface](../2.2-operation-and-interface.md#interfaceの識別とscope)に従う。
|
||||
|
||||
参照の絶対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を導入しない。
|
||||
Self-scopeは許可され、scopeはそのpathのObjectに紐づく。Scope Objectのoptionalな`ref`はProtocolの`scope_ref`として観測・検証する。`ref`がなければidentityを追跡できないことも許容し、scopeの移動追跡や独立したscope validatorは導入しない。
|
||||
|
||||
Conceptualには、eager registrationとlazy registrationの両方が考えられる。
|
||||
登録方法としては、conceptualにはeager registrationとlazy registrationの両方が考えられる。次の例はscope / nameを渡す具体構文を省略しており、暗黙のroot scopeを定めてはいない。登録時のcontextと引数の具体APIも引き続き未決定である。
|
||||
|
||||
```ts
|
||||
const readInterface = host.registerInterface(ReadOnlyFileInterface);
|
||||
@@ -543,9 +543,7 @@ const object = wip.publish(file)
|
||||
});
|
||||
```
|
||||
|
||||
または、Hostが最初のpublication時に各definitionをinternしてよい。
|
||||
|
||||
どちらの場合も次を守る。
|
||||
また、Hostが最初のpublication時に各definitionをinternする方式でもよい。どちらの登録方法でも、次の条件を満たす必要がある。
|
||||
|
||||
- MutableなInterface参照では`fetch_interface`がvalidatorを返し、同じ参照とvalidatorの組から異なるDescriptorを返さない。
|
||||
- Validatorを返さないInterface参照は、その参照の有効期間中Descriptorをimmutableにする。
|
||||
@@ -556,11 +554,13 @@ const object = wip.publish(file)
|
||||
- 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へ影響しないものとして扱う。
|
||||
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を必要とし得る。
|
||||
Operation implementationが必要とする情報は、Object instanceだけとは限らない。Request contextとして、次の情報も必要になり得る。
|
||||
|
||||
- 現在の主体。
|
||||
- `AbortSignal`等のcancellation。
|
||||
@@ -569,34 +569,34 @@ Operation implementationは、Object instance以外にrequest contextを必要
|
||||
- Host serviceやbackend connectionへのaccess。
|
||||
- Resource limit。
|
||||
|
||||
これらをmethod parameter、wrapper object、AsyncLocalStorage、closure等のどれで渡すかは未決定である。Protocol parameterとして宣言された値と、Hostが注入するcontextを明確に区別しなければならない。
|
||||
このcontextをmethod parameter、wrapper object、AsyncLocalStorage、closure等のどれで渡すかは未決定である。どの形でも、Protocol parameterとして宣言された値とHostが注入するcontextは明確に区別する必要がある。
|
||||
|
||||
Object publication時のInterface集合の選択と、Operation call時のmembership、Interface validator、authorizationの再評価を実装できる必要がある。以前の`observe`で公開されたInterface集合だけを根拠に、現在のOperation実行を許可してはならない。
|
||||
Interface集合はObject publication時に選択するだけでなく、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のどこで確認するかは未決定である。
|
||||
Resultのvisibility checkも、通常のstructural Value validationとは別に必要となる。Hostは、Operation resultのRecord、List、Union等にnestedしたすべての`Entry`について、現在の主体にそのpathを認識させてよいかを再帰的に確認できなければならない。この確認をRuntime definition、Projection、Host Runtimeのどこで行うかは未決定である。
|
||||
|
||||
Operation argumentとして`Entry` pathを受け取った場合も、pathの所持をauthorizationの証明として扱わず、参照先へ作用する時点で現在の主体について認可する。
|
||||
Argumentとして受け取った`Entry` pathについても、pathの所持をauthorizationの証明にはしない。参照先へ作用する時点で、現在の主体について認可する。
|
||||
|
||||
## Error model
|
||||
|
||||
TypeScript methodのerror表現は、次の四種類を区別できなければならない。
|
||||
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で明示しなければならない。
|
||||
現在の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しない。
|
||||
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等が必要になる。
|
||||
通常の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で動作できるようにする。
|
||||
Core APIでは、これらを必須にしない。Runtime definition objectをsource of truthとすることで、通常の`tsc`、bundler、JavaScript runtimeで動作できるようにする。
|
||||
|
||||
Build-time toolingはoptionalなauthoring layerとして追加してよい。
|
||||
Build-time toolingは、optionalなauthoring layerとして追加してよい。
|
||||
|
||||
```ts
|
||||
/** @wip-interface */
|
||||
@@ -605,9 +605,9 @@ interface FileOperations {
|
||||
}
|
||||
```
|
||||
|
||||
Toolingがこのdeclarationからruntime definition moduleを生成することはできるが、生成物はcore APIと同じruntime definition semanticsを満たさなければならない。
|
||||
Toolingによってこのdeclarationからruntime definition moduleを生成することはできる。ただし、その生成物もcore APIと同じruntime definition semanticsを満たす必要がある。
|
||||
|
||||
Build-time toolingには少なくとも次の課題がある。
|
||||
Build-time toolingには、少なくとも次の課題がある。
|
||||
|
||||
- TypeScript compiler versionとCompiler APIへの依存。
|
||||
- Bundler、incremental build、package boundaryとの統合。
|
||||
@@ -618,15 +618,15 @@ Build-time toolingには少なくとも次の課題がある。
|
||||
|
||||
## Decoratorをcoreにしない
|
||||
|
||||
Decoratorだけでは、通常はUnion case、Record field、optional、Entry、Integer、nested type、generic、documentation等を完全なruntime schemaとして取得できない。
|
||||
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へ接続する。
|
||||
`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とする。
|
||||
また、class inheritanceをInterface declarationの基本形にすると、複数Interface implementation、plain object、data-drivenなpublicationを不必要に制限することになる。このため、runtime definition objectをcore modelとする。
|
||||
|
||||
## 要件
|
||||
|
||||
TypeScript API modelは少なくとも次を満たす。
|
||||
このdraftのTypeScript API modelに求める要件は、少なくとも次のとおりである。
|
||||
|
||||
- Runtime definition objectをInterface Descriptorとruntime validationのsource of truthにする。
|
||||
- 同じdefinitionからTypeScript implementation typeを推論できる。
|
||||
@@ -656,7 +656,7 @@ TypeScript API modelは少なくとも次を満たす。
|
||||
|
||||
## 非目標
|
||||
|
||||
このdraftは次を決定しない。
|
||||
次の事項は、このdraftでは決定しない。
|
||||
|
||||
- 最終的なpackage、module、function、generic type、class、methodの名前。
|
||||
- 特定のschema libraryまたはvalidation libraryの採用。
|
||||
@@ -698,4 +698,4 @@ TypeScript API modelは少なくとも次を満たす。
|
||||
- TypeScript client APIを同じruntime definitionから生成するか。
|
||||
- Build-time code generationをoptional packageとして提供するか。
|
||||
|
||||
これらを決定する際は、静的service objectだけでなく、Filesystem、database row、virtual aggregate、主体別Interface compositionを同じAPIで実装できるか評価する。
|
||||
これらの選択は、静的service objectに使えるだけでは足りない。Filesystem、database row、virtual aggregate、主体別Interface compositionも、同じAPIで実装できるかを評価して決定する。
|
||||
|
||||
Reference in New Issue
Block a user