@earendil-works/chord 0.86.0 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -16
- package/dist/delta/index.d.ts +6 -4
- package/dist/delta/index.d.ts.map +1 -1
- package/dist/delta/index.js +44 -2
- package/dist/delta/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/provider.d.ts.map +1 -1
- package/dist/services/provider.js +46 -16
- package/dist/services/provider.js.map +1 -1
- package/dist/services/state-internals.d.ts +0 -1
- package/dist/services/state-internals.d.ts.map +1 -1
- package/dist/services/state-internals.js.map +1 -1
- package/dist/services/state.d.ts +2 -2
- package/dist/services/state.d.ts.map +1 -1
- package/dist/services/state.js +105 -32
- package/dist/services/state.js.map +1 -1
- package/dist/state/diff.d.ts +5 -0
- package/dist/state/diff.d.ts.map +1 -0
- package/dist/state/diff.js +259 -0
- package/dist/state/diff.js.map +1 -0
- package/dist/state/draft.d.ts +13 -0
- package/dist/state/draft.d.ts.map +1 -0
- package/dist/state/draft.js +540 -0
- package/dist/state/draft.js.map +1 -0
- package/dist/state/value.d.ts +9 -0
- package/dist/state/value.d.ts.map +1 -0
- package/dist/state/value.js +175 -0
- package/dist/state/value.js.map +1 -0
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/delta/README.md +9 -5
package/dist/index.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export { isRemoteServiceErrorCode, REMOTE_SERVICE_ERROR_CODES, RemoteServiceErro
|
|
|
7
7
|
export { createRemoteServiceEndpoint, type RemoteServiceEndpoint, RemoteServiceProvider, type ServiceUpdatePublisher, } from "./services/provider.ts";
|
|
8
8
|
export { createServiceStateDecoder, createServiceStateEncoder, type ServiceStateDecoder, type ServiceStateEncoder, } from "./services/state-codec.ts";
|
|
9
9
|
export { createServiceCatalogueCall, createServiceSubscribeCall, createServiceUnsubscribeCall, decodeServiceControlCall, parseServiceCall, parseServiceCatalogue, parseServiceProviderUpdate, parseServiceSubscriptionSnapshot, parseWireServiceProviderUpdate, parseWireServiceSubscriptionSnapshot, type ServiceControlCall, type WireServiceInstanceSnapshot, type WireServiceMemberSnapshot, type WireServiceProviderUpdate, type WireServiceSubscriptionSnapshot, } from "./services/wire.ts";
|
|
10
|
+
export type { Draft } from "./state/draft.ts";
|
|
10
11
|
export type { Context, ContextKey, Facet, FacetEnvironment, FacetHost, FacetLoader, FacetOptions, JsonRepresentation, JsonValue, LoadedFacets, MutableReplicatedState, RemoteServiceBinding, RemoteServiceBindingOptions, RemoteServiceSource, RemoteServices, RemoteServiceTransport, ReplicatedState, ReplicatedStateDelivery, Service, ServiceCall, ServiceCatalogueEntry, ServiceInstanceAddress, ServiceInstanceSnapshot, ServiceMemberSnapshot, ServiceMode, ServiceProviderUpdate, ServiceSpawner, ServiceSubscription, ServiceSubscriptionSnapshot, } from "./types.ts";
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,eAAe,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACN,wBAAwB,EACxB,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,sBAAsB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,qBAAqB,EACrB,KAAK,sBAAsB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,yBAAyB,EACzB,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EACrB,0BAA0B,EAC1B,gCAAgC,EAChC,8BAA8B,EAC9B,oCAAoC,EACpC,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,+BAA+B,GACpC,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACX,OAAO,EACP,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,uBAAuB,EACvB,OAAO,EACP,WAAW,EACX,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,2BAA2B,GAC3B,MAAM,YAAY,CAAC","sourcesContent":["/**\n * Chord is a standalone application-composition runtime for agentic applications.\n */\nexport {\n\tcombineFacetLoaders,\n\tcreateFacetHost,\n\tcreateRemoteServiceBinding,\n\tcreateStaticFacetLoader,\n\tdefineFacet,\n\tdefineService,\n\treplicatedState,\n} from \"./api.ts\";\nexport { isJsonValue } from \"./json.ts\";\nexport {\n\tisRemoteServiceErrorCode,\n\tREMOTE_SERVICE_ERROR_CODES,\n\tRemoteServiceError,\n\ttype RemoteServiceErrorCode,\n} from \"./services/errors.ts\";\nexport {\n\tcreateRemoteServiceEndpoint,\n\ttype RemoteServiceEndpoint,\n\tRemoteServiceProvider,\n\ttype ServiceUpdatePublisher,\n} from \"./services/provider.ts\";\nexport {\n\tcreateServiceStateDecoder,\n\tcreateServiceStateEncoder,\n\ttype ServiceStateDecoder,\n\ttype ServiceStateEncoder,\n} from \"./services/state-codec.ts\";\nexport {\n\tcreateServiceCatalogueCall,\n\tcreateServiceSubscribeCall,\n\tcreateServiceUnsubscribeCall,\n\tdecodeServiceControlCall,\n\tparseServiceCall,\n\tparseServiceCatalogue,\n\tparseServiceProviderUpdate,\n\tparseServiceSubscriptionSnapshot,\n\tparseWireServiceProviderUpdate,\n\tparseWireServiceSubscriptionSnapshot,\n\ttype ServiceControlCall,\n\ttype WireServiceInstanceSnapshot,\n\ttype WireServiceMemberSnapshot,\n\ttype WireServiceProviderUpdate,\n\ttype WireServiceSubscriptionSnapshot,\n} from \"./services/wire.ts\";\nexport type {\n\tContext,\n\tContextKey,\n\tFacet,\n\tFacetEnvironment,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tJsonRepresentation,\n\tJsonValue,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceSource,\n\tRemoteServices,\n\tRemoteServiceTransport,\n\tReplicatedState,\n\tReplicatedStateDelivery,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSpawner,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"./types.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,eAAe,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACN,wBAAwB,EACxB,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,sBAAsB,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,qBAAqB,EACrB,KAAK,sBAAsB,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,yBAAyB,EACzB,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EACrB,0BAA0B,EAC1B,gCAAgC,EAChC,8BAA8B,EAC9B,oCAAoC,EACpC,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,+BAA+B,GACpC,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EACX,OAAO,EACP,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,SAAS,EACT,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,uBAAuB,EACvB,OAAO,EACP,WAAW,EACX,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,2BAA2B,GAC3B,MAAM,YAAY,CAAC","sourcesContent":["/**\n * Chord is a standalone application-composition runtime for agentic applications.\n */\nexport {\n\tcombineFacetLoaders,\n\tcreateFacetHost,\n\tcreateRemoteServiceBinding,\n\tcreateStaticFacetLoader,\n\tdefineFacet,\n\tdefineService,\n\treplicatedState,\n} from \"./api.ts\";\nexport { isJsonValue } from \"./json.ts\";\nexport {\n\tisRemoteServiceErrorCode,\n\tREMOTE_SERVICE_ERROR_CODES,\n\tRemoteServiceError,\n\ttype RemoteServiceErrorCode,\n} from \"./services/errors.ts\";\nexport {\n\tcreateRemoteServiceEndpoint,\n\ttype RemoteServiceEndpoint,\n\tRemoteServiceProvider,\n\ttype ServiceUpdatePublisher,\n} from \"./services/provider.ts\";\nexport {\n\tcreateServiceStateDecoder,\n\tcreateServiceStateEncoder,\n\ttype ServiceStateDecoder,\n\ttype ServiceStateEncoder,\n} from \"./services/state-codec.ts\";\nexport {\n\tcreateServiceCatalogueCall,\n\tcreateServiceSubscribeCall,\n\tcreateServiceUnsubscribeCall,\n\tdecodeServiceControlCall,\n\tparseServiceCall,\n\tparseServiceCatalogue,\n\tparseServiceProviderUpdate,\n\tparseServiceSubscriptionSnapshot,\n\tparseWireServiceProviderUpdate,\n\tparseWireServiceSubscriptionSnapshot,\n\ttype ServiceControlCall,\n\ttype WireServiceInstanceSnapshot,\n\ttype WireServiceMemberSnapshot,\n\ttype WireServiceProviderUpdate,\n\ttype WireServiceSubscriptionSnapshot,\n} from \"./services/wire.ts\";\nexport type { Draft } from \"./state/draft.ts\";\nexport type {\n\tContext,\n\tContextKey,\n\tFacet,\n\tFacetEnvironment,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tJsonRepresentation,\n\tJsonValue,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceSource,\n\tRemoteServices,\n\tRemoteServiceTransport,\n\tReplicatedState,\n\tReplicatedStateDelivery,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSpawner,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"./types.ts\";\n"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,eAAe,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACN,wBAAwB,EACxB,0BAA0B,EAC1B,kBAAkB,GAElB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,2BAA2B,EAE3B,qBAAqB,GAErB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,yBAAyB,EACzB,yBAAyB,GAGzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EACrB,0BAA0B,EAC1B,gCAAgC,EAChC,8BAA8B,EAC9B,oCAAoC,GAMpC,MAAM,oBAAoB,CAAC","sourcesContent":["/**\n * Chord is a standalone application-composition runtime for agentic applications.\n */\nexport {\n\tcombineFacetLoaders,\n\tcreateFacetHost,\n\tcreateRemoteServiceBinding,\n\tcreateStaticFacetLoader,\n\tdefineFacet,\n\tdefineService,\n\treplicatedState,\n} from \"./api.ts\";\nexport { isJsonValue } from \"./json.ts\";\nexport {\n\tisRemoteServiceErrorCode,\n\tREMOTE_SERVICE_ERROR_CODES,\n\tRemoteServiceError,\n\ttype RemoteServiceErrorCode,\n} from \"./services/errors.ts\";\nexport {\n\tcreateRemoteServiceEndpoint,\n\ttype RemoteServiceEndpoint,\n\tRemoteServiceProvider,\n\ttype ServiceUpdatePublisher,\n} from \"./services/provider.ts\";\nexport {\n\tcreateServiceStateDecoder,\n\tcreateServiceStateEncoder,\n\ttype ServiceStateDecoder,\n\ttype ServiceStateEncoder,\n} from \"./services/state-codec.ts\";\nexport {\n\tcreateServiceCatalogueCall,\n\tcreateServiceSubscribeCall,\n\tcreateServiceUnsubscribeCall,\n\tdecodeServiceControlCall,\n\tparseServiceCall,\n\tparseServiceCatalogue,\n\tparseServiceProviderUpdate,\n\tparseServiceSubscriptionSnapshot,\n\tparseWireServiceProviderUpdate,\n\tparseWireServiceSubscriptionSnapshot,\n\ttype ServiceControlCall,\n\ttype WireServiceInstanceSnapshot,\n\ttype WireServiceMemberSnapshot,\n\ttype WireServiceProviderUpdate,\n\ttype WireServiceSubscriptionSnapshot,\n} from \"./services/wire.ts\";\nexport type {\n\tContext,\n\tContextKey,\n\tFacet,\n\tFacetEnvironment,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tJsonRepresentation,\n\tJsonValue,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceSource,\n\tRemoteServices,\n\tRemoteServiceTransport,\n\tReplicatedState,\n\tReplicatedStateDelivery,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSpawner,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"./types.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,0BAA0B,EAC1B,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,eAAe,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACN,wBAAwB,EACxB,0BAA0B,EAC1B,kBAAkB,GAElB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,2BAA2B,EAE3B,qBAAqB,GAErB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,yBAAyB,EACzB,yBAAyB,GAGzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,0BAA0B,EAC1B,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EACrB,0BAA0B,EAC1B,gCAAgC,EAChC,8BAA8B,EAC9B,oCAAoC,GAMpC,MAAM,oBAAoB,CAAC","sourcesContent":["/**\n * Chord is a standalone application-composition runtime for agentic applications.\n */\nexport {\n\tcombineFacetLoaders,\n\tcreateFacetHost,\n\tcreateRemoteServiceBinding,\n\tcreateStaticFacetLoader,\n\tdefineFacet,\n\tdefineService,\n\treplicatedState,\n} from \"./api.ts\";\nexport { isJsonValue } from \"./json.ts\";\nexport {\n\tisRemoteServiceErrorCode,\n\tREMOTE_SERVICE_ERROR_CODES,\n\tRemoteServiceError,\n\ttype RemoteServiceErrorCode,\n} from \"./services/errors.ts\";\nexport {\n\tcreateRemoteServiceEndpoint,\n\ttype RemoteServiceEndpoint,\n\tRemoteServiceProvider,\n\ttype ServiceUpdatePublisher,\n} from \"./services/provider.ts\";\nexport {\n\tcreateServiceStateDecoder,\n\tcreateServiceStateEncoder,\n\ttype ServiceStateDecoder,\n\ttype ServiceStateEncoder,\n} from \"./services/state-codec.ts\";\nexport {\n\tcreateServiceCatalogueCall,\n\tcreateServiceSubscribeCall,\n\tcreateServiceUnsubscribeCall,\n\tdecodeServiceControlCall,\n\tparseServiceCall,\n\tparseServiceCatalogue,\n\tparseServiceProviderUpdate,\n\tparseServiceSubscriptionSnapshot,\n\tparseWireServiceProviderUpdate,\n\tparseWireServiceSubscriptionSnapshot,\n\ttype ServiceControlCall,\n\ttype WireServiceInstanceSnapshot,\n\ttype WireServiceMemberSnapshot,\n\ttype WireServiceProviderUpdate,\n\ttype WireServiceSubscriptionSnapshot,\n} from \"./services/wire.ts\";\nexport type { Draft } from \"./state/draft.ts\";\nexport type {\n\tContext,\n\tContextKey,\n\tFacet,\n\tFacetEnvironment,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tJsonRepresentation,\n\tJsonValue,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceSource,\n\tRemoteServices,\n\tRemoteServiceTransport,\n\tReplicatedState,\n\tReplicatedStateDelivery,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSpawner,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"./types.ts\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/services/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,OAAO,EACP,SAAS,EACT,qBAAqB,EACrB,OAAO,EACP,WAAW,EACX,qBAAqB,EAIrB,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EAEnB,MAAM,aAAa,CAAC;AAmCrB,UAAU,yBAAyB;IAClC,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC3B;AAYD,MAAM,MAAM,sBAAsB,GAAG,CACpC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,OAAO,KACZ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,yFAAyF;AACzF,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAC7G,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,qBAAa,qBAAqB;;IAKjC,YAAY,OAAO,EAAE,SAAS,CAAC,yBAAyB,GAAG;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,EAuBpF;IAED,IAAI,SAAS,IAAI,SAAS,qBAAqB,EAAE,CAEhD;IAED,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAavF;IAED,yFAAyF;IACzF,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAWrC;IAED,0EAA0E;IAC1E,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAOnG;IAED,iFAAiF;IACjF,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAiBvF;IAED,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAS7B;IAED,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CA6BxG;IAEK,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAuBhF;IAED,SAAS,CACR,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GACjE,mBAAmB,CA2CrB;IAED,OAAO,IAAI,IAAI,CAuCd;CA+KD;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,qBAAqB,CAoClG;AAED,wBAAgB,mCAAmC,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,IAAI,CAEpG","sourcesContent":["import type {\n\tContext,\n\tJsonValue,\n\tRemoteServiceContract,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"../types.ts\";\nimport { RemoteServiceError } from \"./errors.ts\";\nimport { serviceDeliveryContext } from \"./state.ts\";\nimport { getReplicatedStateInternals, type ReplicatedStateInternals } from \"./state-internals.ts\";\nimport { decodeServiceControlCall } from \"./wire.ts\";\n\ntype RemoteMethod = (...args: unknown[]) => unknown;\n\ntype InstanceMember =\n\t| { readonly kind: \"method\"; readonly method: RemoteMethod }\n\t| { readonly kind: \"state\"; readonly state: ReplicatedStateInternals };\n\ntype ServiceMemberKind = InstanceMember[\"kind\"];\n\ninterface ClassifiedRemoteServiceImplementation {\n\treadonly implementation: object;\n\treadonly members: Map<string, InstanceMember>;\n}\n\ninterface ProviderInstance {\n\treadonly address?: ServiceInstanceAddress;\n\treadonly implementation: object;\n\treadonly members: ReadonlyMap<string, InstanceMember>;\n\treadonly removeMemberListeners: readonly (() => void)[];\n\tactive: boolean;\n}\n\ninterface ProviderSubscriber {\n\treadonly listener: (update: ServiceProviderUpdate, context: Context) => void;\n\treadonly buffer: { readonly update: ServiceProviderUpdate; readonly context: Context }[];\n\tactive: boolean;\n\tterminated: boolean;\n\tclosed: boolean;\n}\n\ninterface ServiceProviderDefinition {\n\treadonly service: { readonly id: string; readonly local?: boolean };\n\treadonly mode: ServiceMode;\n}\n\ninterface ServiceRegistration {\n\treadonly serviceId: string;\n\treadonly mode: ServiceMode;\n\tsingleton?: ProviderInstance;\n\tsingletonShape?: ReadonlyMap<string, ServiceMemberKind>;\n\treadonly instances: Map<string, ProviderInstance>;\n\treadonly generations: Map<string, number>;\n\treadonly subscribers: Set<ProviderSubscriber>;\n}\n\nexport type ServiceUpdatePublisher = (\n\tsubscriptionId: string,\n\tupdate: ServiceProviderUpdate,\n\tcontext: Context,\n) => void | Promise<void>;\n\n/** Hosts one provider for one remote consumer and owns that consumer's subscriptions. */\nexport interface RemoteServiceEndpoint {\n\tinvoke(call: ServiceCall, publish: ServiceUpdatePublisher, context: Context): Promise<JsonValue | undefined>;\n\tdispose(): void;\n}\n\nexport class RemoteServiceProvider {\n\treadonly #catalogue: readonly ServiceCatalogueEntry[];\n\treadonly #registrations = new Map<string, ServiceRegistration>();\n\t#disposed = false;\n\n\tconstructor(entries: readonly (ServiceProviderDefinition | { readonly id: string })[]) {\n\t\tconst definitions = entries.map(\n\t\t\t(entry): ServiceProviderDefinition => (\"service\" in entry ? entry : { service: entry, mode: \"singleton\" }),\n\t\t);\n\t\tfor (const { service } of definitions) {\n\t\t\tif (\"local\" in service && service.local === true) {\n\t\t\t\tthrow new TypeError(`Local service ${service.id} cannot be published remotely`);\n\t\t\t}\n\t\t}\n\t\tconst ids = definitions.map(({ service }) => service.id);\n\t\tif (new Set(ids).size !== ids.length) throw new TypeError(\"Remote service catalogue contains duplicate IDs\");\n\t\tthis.#catalogue = Object.freeze(\n\t\t\tdefinitions.map(({ service, mode }) => Object.freeze({ serviceId: service.id, mode })),\n\t\t);\n\t\tfor (const { service, mode } of definitions) {\n\t\t\tthis.#registrations.set(service.id, {\n\t\t\t\tserviceId: service.id,\n\t\t\t\tmode,\n\t\t\t\tinstances: new Map(),\n\t\t\t\tgenerations: new Map(),\n\t\t\t\tsubscribers: new Set(),\n\t\t\t});\n\t\t}\n\t}\n\n\tget catalogue(): readonly ServiceCatalogueEntry[] {\n\t\treturn this.#catalogue;\n\t}\n\n\tprovide<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tif (registration.singleton !== undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${service.id} already has a provider`);\n\t\t}\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tregistration.singleton = this.#createInstance(registration, classified, undefined);\n\t\tregistration.singletonShape = shape;\n\t}\n\n\t/** Disconnect one singleton while preserving active subscriptions and remote facades. */\n\twithdraw<T>(service: Service<T>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst previous = registration.singleton;\n\t\tif (previous === undefined) return;\n\t\tprevious.active = false;\n\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\tdelete registration.singleton;\n\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t}\n\n\t/** Check a singleton replacement without changing the active provider. */\n\tvalidateReplacement<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tthis.#assertSingletonShape(registration, serviceMemberShape(classified.members));\n\t}\n\n\t/** Replace one singleton without making its stable remote facade unavailable. */\n\treplace<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tconst replacement = this.#createInstance(registration, classified, undefined);\n\t\tconst previous = registration.singleton;\n\t\tif (previous !== undefined) {\n\t\t\tprevious.active = false;\n\t\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\t}\n\t\tregistration.singleton = replacement;\n\t\tregistration.singletonShape = shape;\n\t\tthis.#emit(registration, { type: \"replaced\", snapshot: this.#snapshotInstance(replacement) });\n\t}\n\n\tuse<T>(service: Service<T>): T {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registrations.get(service.id);\n\t\tif (registration?.mode !== \"singleton\" || registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${service.id} has no local provider`);\n\t\t}\n\t\treturn registration.singleton.implementation as T;\n\t}\n\n\tspawn<T>(service: Service<T>, key: string, implementation: NoInfer<RemoteServiceContract<T>>): () => void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tif (key.length === 0) throw new TypeError(\"Remote service instance key must not be empty\");\n\t\tconst registration = this.#registration(service.id, \"keyed\");\n\t\tif (registration.instances.has(key)) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${service.id} already has a live instance with key ${key}`,\n\t\t\t);\n\t\t}\n\t\tconst generation = (registration.generations.get(key) ?? 0) + 1;\n\t\tregistration.generations.set(key, generation);\n\t\tconst address = { key, generation } satisfies ServiceInstanceAddress;\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst instance = this.#createInstance(registration, classified, address);\n\t\tregistration.instances.set(key, instance);\n\t\tthis.#emit(registration, { type: \"spawned\", instance: this.#snapshotInstance(instance) });\n\t\tlet closed = false;\n\t\treturn () => {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tif (registration.instances.get(key) !== instance) return;\n\t\t\tinstance.active = false;\n\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\tregistration.instances.delete(key);\n\t\t\tthis.#emit(registration, { type: \"closed\", instance: address });\n\t\t};\n\t}\n\n\tasync invoke(call: ServiceCall, context: Context): Promise<JsonValue | undefined> {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(call.serviceId);\n\t\tconst registration = this.#registrations.get(call.serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${call.serviceId}`);\n\t\t}\n\t\tconst instance = this.#resolveInstance(registration, call.instance);\n\t\tconst member = instance.members.get(call.member);\n\t\tif (member === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_not_found\",\n\t\t\t\t`Unknown remote service member ${call.serviceId}.${call.member}`,\n\t\t\t);\n\t\t}\n\t\tif (member.kind !== \"method\") {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_mismatch\",\n\t\t\t\t`Remote service member ${call.serviceId}.${call.member} is not a method`,\n\t\t\t);\n\t\t}\n\t\tconst result: unknown = await Reflect.apply(member.method, instance.implementation, [...call.args, context]);\n\t\treturn result as JsonValue | undefined;\n\t}\n\n\tsubscribe(\n\t\tserviceId: string,\n\t\tmode: ServiceMode,\n\t\tlistener: (update: ServiceProviderUpdate, context: Context) => void,\n\t): ServiceSubscription {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(serviceId);\n\t\tconst registration = this.#registration(serviceId, mode);\n\t\tif (registration.mode === \"singleton\" && registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${serviceId} has no provider`);\n\t\t}\n\t\tconst subscriber: ProviderSubscriber = {\n\t\t\tlistener,\n\t\t\tbuffer: [],\n\t\t\tactive: false,\n\t\t\tterminated: false,\n\t\t\tclosed: false,\n\t\t};\n\t\tthis.#publishPending(registration);\n\t\tregistration.subscribers.add(subscriber);\n\t\tconst snapshot = this.#snapshot(registration);\n\t\treturn {\n\t\t\tsnapshot,\n\t\t\tactivate: () => {\n\t\t\t\tif (subscriber.closed || subscriber.active) return;\n\t\t\t\tsubscriber.active = true;\n\t\t\t\tconst errors: unknown[] = [];\n\t\t\t\ttry {\n\t\t\t\t\tfor (const entry of subscriber.buffer.splice(0)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlistener(entry.update, entry.context);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tif (subscriber.terminated) subscriber.closed = true;\n\t\t\t\t}\n\t\t\t\tthrowCollectedErrors(errors, \"Failed to activate remote service subscription\");\n\t\t\t},\n\t\t\tclose: () => {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tsubscriber.closed = true;\n\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\tregistration.subscribers.delete(subscriber);\n\t\t\t},\n\t\t};\n\t}\n\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\tconst errors: unknown[] = [];\n\t\tfor (const registration of this.#registrations.values()) {\n\t\t\tconst singleton = registration.singleton;\n\t\t\tif (singleton !== undefined) {\n\t\t\t\tsingleton.active = false;\n\t\t\t\tfor (const remove of singleton.removeMemberListeners) remove();\n\t\t\t\tdelete registration.singleton;\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const [key, instance] of [...registration.instances]) {\n\t\t\t\tinstance.active = false;\n\t\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\t\tregistration.instances.delete(key);\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"closed\", instance: instance.address! });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const subscriber of registration.subscribers) {\n\t\t\t\tif (subscriber.active) {\n\t\t\t\t\tsubscriber.closed = true;\n\t\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\t} else {\n\t\t\t\t\tsubscriber.terminated = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tregistration.subscribers.clear();\n\t\t}\n\t\tthis.#registrations.clear();\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose remote service provider\");\n\t}\n\n\t#registration(serviceId: string, mode: ServiceMode): ServiceRegistration {\n\t\tconst registration = this.#registrations.get(serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${serviceId}`);\n\t\t}\n\t\tif (registration.mode !== mode) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${serviceId} is ${registration.mode}, not ${mode}`,\n\t\t\t);\n\t\t}\n\t\treturn registration;\n\t}\n\n\t#createInstance(\n\t\tregistration: ServiceRegistration,\n\t\tclassified: ClassifiedRemoteServiceImplementation,\n\t\taddress: ServiceInstanceAddress | undefined,\n\t): ProviderInstance {\n\t\tconst removeMemberListeners: (() => void)[] = [];\n\t\tconst instance: ProviderInstance = {\n\t\t\t...(address === undefined ? {} : { address }),\n\t\t\timplementation: classified.implementation,\n\t\t\tmembers: classified.members,\n\t\t\tremoveMemberListeners,\n\t\t\tactive: true,\n\t\t};\n\t\tfor (const [name, member] of classified.members) {\n\t\t\tif (member.kind !== \"state\") continue;\n\t\t\tremoveMemberListeners.push(\n\t\t\t\tmember.state.subscribe((ops, sequence, context) => {\n\t\t\t\t\tif (!instance.active) return;\n\t\t\t\t\tthis.#emit(\n\t\t\t\t\t\tregistration,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"state\",\n\t\t\t\t\t\t\t...(address === undefined ? {} : { instance: address }),\n\t\t\t\t\t\t\tmember: name,\n\t\t\t\t\t\t\tsequence,\n\t\t\t\t\t\t\tops,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#assertSingletonShape(registration: ServiceRegistration, replacement: ReadonlyMap<string, ServiceMemberKind>): void {\n\t\tconst current = registration.singletonShape;\n\t\tif (current === undefined || sameServiceMemberShape(current, replacement)) return;\n\t\tthrow new RemoteServiceError(\n\t\t\t\"service_member_mismatch\",\n\t\t\t`Remote service ${registration.serviceId} replacement must preserve its member shape`,\n\t\t);\n\t}\n\n\t#resolveInstance(registration: ServiceRegistration, address: ServiceInstanceAddress | undefined): ProviderInstance {\n\t\tif (registration.mode === \"singleton\") {\n\t\t\tif (address !== undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t\t`Remote service ${registration.serviceId} is singleton`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (registration.singleton === undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_not_found\",\n\t\t\t\t\t`Remote service ${registration.serviceId} has no provider`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn registration.singleton;\n\t\t}\n\t\tif (address === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${registration.serviceId} is keyed`);\n\t\t}\n\t\tconst instance = registration.instances.get(address.key);\n\t\tif (instance === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_instance_not_found\",\n\t\t\t\t`Remote service ${registration.serviceId} has no instance ${address.key}`,\n\t\t\t);\n\t\t}\n\t\tif (instance.address?.generation !== address.generation) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_stale_instance\",\n\t\t\t\t`Remote service ${registration.serviceId} instance ${address.key} is stale`,\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#publishPending(registration: ServiceRegistration): void {\n\t\tconst context = serviceDeliveryContext();\n\t\tconst instances =\n\t\t\tregistration.mode === \"singleton\"\n\t\t\t\t? registration.singleton === undefined\n\t\t\t\t\t? []\n\t\t\t\t\t: [registration.singleton]\n\t\t\t\t: registration.instances.values();\n\t\tfor (const instance of instances) {\n\t\t\tfor (const member of instance.members.values()) {\n\t\t\t\tif (member.kind === \"state\") member.state.publish(context);\n\t\t\t}\n\t\t}\n\t}\n\n\t#snapshot(registration: ServiceRegistration): ServiceSubscriptionSnapshot {\n\t\tconst instances =\n\t\t\tregistration.mode === \"singleton\"\n\t\t\t\t? registration.singleton\n\t\t\t\t\t? [this.#snapshotInstance(registration.singleton)]\n\t\t\t\t\t: []\n\t\t\t\t: [...registration.instances.values()]\n\t\t\t\t\t\t.sort((left, right) => left.address!.key.localeCompare(right.address!.key))\n\t\t\t\t\t\t.map((instance) => this.#snapshotInstance(instance));\n\t\treturn { serviceId: registration.serviceId, mode: registration.mode, instances };\n\t}\n\n\t#snapshotInstance(instance: ProviderInstance): ServiceInstanceSnapshot {\n\t\tconst members: ServiceMemberSnapshot[] = [];\n\t\tfor (const [name, member] of instance.members) {\n\t\t\tif (member.kind === \"method\") {\n\t\t\t\tmembers.push({ name, kind: \"method\" });\n\t\t\t} else {\n\t\t\t\tmembers.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind: \"state\",\n\t\t\t\t\tsequence: member.state.sequence,\n\t\t\t\t\tops: [[\"r\", member.state.value as JsonValue]],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\t...(instance.address === undefined ? {} : { instance: instance.address }),\n\t\t\tmembers,\n\t\t};\n\t}\n\n\t#emit(registration: ServiceRegistration, update: ServiceProviderUpdate, context?: Context): void {\n\t\tif (registration.subscribers.size === 0) return;\n\t\tconst deliveryContext = context ?? serviceDeliveryContext();\n\t\tconst errors: unknown[] = [];\n\t\tfor (const subscriber of registration.subscribers) {\n\t\t\tif (subscriber.closed) continue;\n\t\t\tconst entry = { update, context: deliveryContext };\n\t\t\tif (!subscriber.active) {\n\t\t\t\tsubscriber.buffer.push(entry);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsubscriber.listener(entry.update, entry.context);\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(error);\n\t\t\t}\n\t\t}\n\t\tthrowCollectedErrors(errors, `Failed to publish remote service ${registration.serviceId} update`);\n\t}\n\n\t#assertRemotable(service: { readonly id: string; readonly local: boolean }): void {\n\t\tif (service.local) throw new RemoteServiceError(\"service_not_allowed\", `Service ${service.id} is process-local`);\n\t}\n\n\t#assertAllowed(serviceId: string): void {\n\t\tif (!this.#registrations.has(serviceId)) {\n\t\t\tthrow new RemoteServiceError(\"service_not_allowed\", `Remote service ${serviceId} is not allowlisted`);\n\t\t}\n\t}\n\n\t#assertActive(): void {\n\t\tif (this.#disposed) throw new Error(\"Remote service provider is disposed\");\n\t}\n}\n\nexport function createRemoteServiceEndpoint(provider: RemoteServiceProvider): RemoteServiceEndpoint {\n\tconst subscriptions = new Map<string, ServiceSubscription>();\n\tlet disposed = false;\n\n\treturn {\n\t\tasync invoke(call, publish, context) {\n\t\t\tif (disposed) throw new Error(\"Remote service endpoint is disposed\");\n\t\t\tconst control = decodeServiceControlCall(call);\n\t\t\tif (control?.type === \"catalogue\") return provider.catalogue as unknown as JsonValue;\n\t\t\tif (control?.type === \"subscribe\") {\n\t\t\t\tif (subscriptions.has(control.subscriptionId)) {\n\t\t\t\t\tthrow new Error(\"Service subscription ID is already active\");\n\t\t\t\t}\n\t\t\t\tconst subscription = provider.subscribe(control.serviceId, control.mode, (update, updateContext) => {\n\t\t\t\t\tvoid Promise.resolve(publish(control.subscriptionId, update, updateContext)).catch(() => {});\n\t\t\t\t});\n\t\t\t\tsubscriptions.set(control.subscriptionId, subscription);\n\t\t\t\tsubscription.activate();\n\t\t\t\treturn subscription.snapshot as unknown as JsonValue;\n\t\t\t}\n\t\t\tif (control?.type === \"unsubscribe\") {\n\t\t\t\tconst subscription = subscriptions.get(control.subscriptionId);\n\t\t\t\tif (subscription === undefined) throw new Error(\"Service subscription was not found\");\n\t\t\t\tsubscription.close();\n\t\t\t\tsubscriptions.delete(control.subscriptionId);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn provider.invoke(call, context);\n\t\t},\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tfor (const subscription of subscriptions.values()) subscription.close();\n\t\t\tsubscriptions.clear();\n\t\t},\n\t};\n}\n\nexport function validateRemoteServiceImplementation(serviceId: string, implementation: unknown): void {\n\tvoid classifyRemoteServiceImplementation(serviceId, implementation);\n}\n\nfunction classifyRemoteServiceImplementation(\n\tserviceId: string,\n\timplementation: unknown,\n): ClassifiedRemoteServiceImplementation {\n\tif (typeof implementation !== \"object\" || implementation === null || Array.isArray(implementation)) {\n\t\tthrow new TypeError(`Remote service ${serviceId} implementation must be an object`);\n\t}\n\tconst members = new Map<string, InstanceMember>();\n\tfor (const name of Object.keys(implementation).sort()) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(implementation, name);\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} must be a data property`);\n\t\t}\n\t\tif (typeof descriptor.value === \"function\") {\n\t\t\tmembers.set(name, { kind: \"method\", method: descriptor.value as RemoteMethod });\n\t\t\tcontinue;\n\t\t}\n\t\tconst state = getReplicatedStateInternals(descriptor.value);\n\t\tif (state !== undefined) {\n\t\t\tmembers.set(name, { kind: \"state\", state });\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} is not remotely exposable`);\n\t}\n\tif (members.size === 0) throw new TypeError(`Remote service ${serviceId} has no members`);\n\treturn { implementation, members };\n}\n\nfunction throwCollectedErrors(errors: readonly unknown[], message: string): void {\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, message);\n}\n\nfunction serviceMemberShape(members: ReadonlyMap<string, InstanceMember>): ReadonlyMap<string, ServiceMemberKind> {\n\treturn new Map([...members].map(([name, member]) => [name, member.kind]));\n}\n\nfunction sameServiceMemberShape(\n\tleft: ReadonlyMap<string, ServiceMemberKind>,\n\tright: ReadonlyMap<string, ServiceMemberKind>,\n): boolean {\n\treturn left.size === right.size && [...left].every(([name, kind]) => right.get(name) === kind);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/services/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,OAAO,EACP,SAAS,EACT,qBAAqB,EACrB,OAAO,EACP,WAAW,EACX,qBAAqB,EAIrB,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EAEnB,MAAM,aAAa,CAAC;AAoCrB,UAAU,yBAAyB;IAClC,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC3B;AAYD,MAAM,MAAM,sBAAsB,GAAG,CACpC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,OAAO,KACZ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,yFAAyF;AACzF,MAAM,WAAW,qBAAqB;IACrC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAC7G,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,qBAAa,qBAAqB;;IAKjC,YAAY,OAAO,EAAE,SAAS,CAAC,yBAAyB,GAAG;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,EAuBpF;IAED,IAAI,SAAS,IAAI,SAAS,qBAAqB,EAAE,CAEhD;IAED,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAavF;IAED,yFAAyF;IACzF,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAWrC;IAED,0EAA0E;IAC1E,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAOnG;IAED,iFAAiF;IACjF,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAiBvF;IAED,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAS7B;IAED,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CA6BxG;IAEK,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAuBhF;IAED,SAAS,CACR,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GACjE,mBAAmB,CA4CrB;IAED,OAAO,IAAI,IAAI,CAuCd;CAgKD;AA4CD,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,qBAAqB,CAoClG;AAED,wBAAgB,mCAAmC,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,IAAI,CAEpG","sourcesContent":["import type {\n\tContext,\n\tJsonValue,\n\tRemoteServiceContract,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"../types.ts\";\nimport { RemoteServiceError } from \"./errors.ts\";\nimport { serviceDeliveryContext } from \"./state.ts\";\nimport { getReplicatedStateInternals, type ReplicatedStateInternals } from \"./state-internals.ts\";\nimport { decodeServiceControlCall } from \"./wire.ts\";\n\ntype RemoteMethod = (...args: unknown[]) => unknown;\n\ntype InstanceMember =\n\t| { readonly kind: \"method\"; readonly method: RemoteMethod }\n\t| { readonly kind: \"state\"; readonly state: ReplicatedStateInternals };\n\ntype ServiceMemberKind = InstanceMember[\"kind\"];\n\ninterface ClassifiedRemoteServiceImplementation {\n\treadonly implementation: object;\n\treadonly members: Map<string, InstanceMember>;\n}\n\ninterface ProviderInstance {\n\treadonly address?: ServiceInstanceAddress;\n\treadonly implementation: object;\n\treadonly members: ReadonlyMap<string, InstanceMember>;\n\treadonly removeMemberListeners: readonly (() => void)[];\n\tactive: boolean;\n}\n\ninterface ProviderSubscriber {\n\treadonly listener: (update: ServiceProviderUpdate, context: Context) => void;\n\treadonly buffer: { readonly update: ServiceProviderUpdate; readonly context: Context }[];\n\treadonly snapshotSequences: Map<string, number>;\n\tactive: boolean;\n\tterminated: boolean;\n\tclosed: boolean;\n}\n\ninterface ServiceProviderDefinition {\n\treadonly service: { readonly id: string; readonly local?: boolean };\n\treadonly mode: ServiceMode;\n}\n\ninterface ServiceRegistration {\n\treadonly serviceId: string;\n\treadonly mode: ServiceMode;\n\tsingleton?: ProviderInstance;\n\tsingletonShape?: ReadonlyMap<string, ServiceMemberKind>;\n\treadonly instances: Map<string, ProviderInstance>;\n\treadonly generations: Map<string, number>;\n\treadonly subscribers: Set<ProviderSubscriber>;\n}\n\nexport type ServiceUpdatePublisher = (\n\tsubscriptionId: string,\n\tupdate: ServiceProviderUpdate,\n\tcontext: Context,\n) => void | Promise<void>;\n\n/** Hosts one provider for one remote consumer and owns that consumer's subscriptions. */\nexport interface RemoteServiceEndpoint {\n\tinvoke(call: ServiceCall, publish: ServiceUpdatePublisher, context: Context): Promise<JsonValue | undefined>;\n\tdispose(): void;\n}\n\nexport class RemoteServiceProvider {\n\treadonly #catalogue: readonly ServiceCatalogueEntry[];\n\treadonly #registrations = new Map<string, ServiceRegistration>();\n\t#disposed = false;\n\n\tconstructor(entries: readonly (ServiceProviderDefinition | { readonly id: string })[]) {\n\t\tconst definitions = entries.map(\n\t\t\t(entry): ServiceProviderDefinition => (\"service\" in entry ? entry : { service: entry, mode: \"singleton\" }),\n\t\t);\n\t\tfor (const { service } of definitions) {\n\t\t\tif (\"local\" in service && service.local === true) {\n\t\t\t\tthrow new TypeError(`Local service ${service.id} cannot be published remotely`);\n\t\t\t}\n\t\t}\n\t\tconst ids = definitions.map(({ service }) => service.id);\n\t\tif (new Set(ids).size !== ids.length) throw new TypeError(\"Remote service catalogue contains duplicate IDs\");\n\t\tthis.#catalogue = Object.freeze(\n\t\t\tdefinitions.map(({ service, mode }) => Object.freeze({ serviceId: service.id, mode })),\n\t\t);\n\t\tfor (const { service, mode } of definitions) {\n\t\t\tthis.#registrations.set(service.id, {\n\t\t\t\tserviceId: service.id,\n\t\t\t\tmode,\n\t\t\t\tinstances: new Map(),\n\t\t\t\tgenerations: new Map(),\n\t\t\t\tsubscribers: new Set(),\n\t\t\t});\n\t\t}\n\t}\n\n\tget catalogue(): readonly ServiceCatalogueEntry[] {\n\t\treturn this.#catalogue;\n\t}\n\n\tprovide<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tif (registration.singleton !== undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${service.id} already has a provider`);\n\t\t}\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tregistration.singleton = this.#createInstance(registration, classified, undefined);\n\t\tregistration.singletonShape = shape;\n\t}\n\n\t/** Disconnect one singleton while preserving active subscriptions and remote facades. */\n\twithdraw<T>(service: Service<T>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst previous = registration.singleton;\n\t\tif (previous === undefined) return;\n\t\tprevious.active = false;\n\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\tdelete registration.singleton;\n\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t}\n\n\t/** Check a singleton replacement without changing the active provider. */\n\tvalidateReplacement<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tthis.#assertSingletonShape(registration, serviceMemberShape(classified.members));\n\t}\n\n\t/** Replace one singleton without making its stable remote facade unavailable. */\n\treplace<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tconst replacement = this.#createInstance(registration, classified, undefined);\n\t\tconst previous = registration.singleton;\n\t\tif (previous !== undefined) {\n\t\t\tprevious.active = false;\n\t\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\t}\n\t\tregistration.singleton = replacement;\n\t\tregistration.singletonShape = shape;\n\t\tthis.#emit(registration, { type: \"replaced\", snapshot: this.#snapshotInstance(replacement) });\n\t}\n\n\tuse<T>(service: Service<T>): T {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registrations.get(service.id);\n\t\tif (registration?.mode !== \"singleton\" || registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${service.id} has no local provider`);\n\t\t}\n\t\treturn registration.singleton.implementation as T;\n\t}\n\n\tspawn<T>(service: Service<T>, key: string, implementation: NoInfer<RemoteServiceContract<T>>): () => void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tif (key.length === 0) throw new TypeError(\"Remote service instance key must not be empty\");\n\t\tconst registration = this.#registration(service.id, \"keyed\");\n\t\tif (registration.instances.has(key)) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${service.id} already has a live instance with key ${key}`,\n\t\t\t);\n\t\t}\n\t\tconst generation = (registration.generations.get(key) ?? 0) + 1;\n\t\tregistration.generations.set(key, generation);\n\t\tconst address = { key, generation } satisfies ServiceInstanceAddress;\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst instance = this.#createInstance(registration, classified, address);\n\t\tregistration.instances.set(key, instance);\n\t\tthis.#emit(registration, { type: \"spawned\", instance: this.#snapshotInstance(instance) });\n\t\tlet closed = false;\n\t\treturn () => {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tif (registration.instances.get(key) !== instance) return;\n\t\t\tinstance.active = false;\n\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\tregistration.instances.delete(key);\n\t\t\tthis.#emit(registration, { type: \"closed\", instance: address });\n\t\t};\n\t}\n\n\tasync invoke(call: ServiceCall, context: Context): Promise<JsonValue | undefined> {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(call.serviceId);\n\t\tconst registration = this.#registrations.get(call.serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${call.serviceId}`);\n\t\t}\n\t\tconst instance = this.#resolveInstance(registration, call.instance);\n\t\tconst member = instance.members.get(call.member);\n\t\tif (member === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_not_found\",\n\t\t\t\t`Unknown remote service member ${call.serviceId}.${call.member}`,\n\t\t\t);\n\t\t}\n\t\tif (member.kind !== \"method\") {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_mismatch\",\n\t\t\t\t`Remote service member ${call.serviceId}.${call.member} is not a method`,\n\t\t\t);\n\t\t}\n\t\tconst result: unknown = await Reflect.apply(member.method, instance.implementation, [...call.args, context]);\n\t\treturn result as JsonValue | undefined;\n\t}\n\n\tsubscribe(\n\t\tserviceId: string,\n\t\tmode: ServiceMode,\n\t\tlistener: (update: ServiceProviderUpdate, context: Context) => void,\n\t): ServiceSubscription {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(serviceId);\n\t\tconst registration = this.#registration(serviceId, mode);\n\t\tif (registration.mode === \"singleton\" && registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${serviceId} has no provider`);\n\t\t}\n\t\tconst subscriber: ProviderSubscriber = {\n\t\t\tlistener,\n\t\t\tbuffer: [],\n\t\t\tsnapshotSequences: new Map(),\n\t\t\tactive: false,\n\t\t\tterminated: false,\n\t\t\tclosed: false,\n\t\t};\n\t\tregistration.subscribers.add(subscriber);\n\t\tconst snapshot = this.#snapshot(registration);\n\t\trecordSnapshotSequences(subscriber.snapshotSequences, snapshot.instances);\n\t\treturn {\n\t\t\tsnapshot,\n\t\t\tactivate: () => {\n\t\t\t\tif (subscriber.closed || subscriber.active) return;\n\t\t\t\tsubscriber.active = true;\n\t\t\t\tconst errors: unknown[] = [];\n\t\t\t\ttry {\n\t\t\t\t\tfor (const entry of subscriber.buffer.splice(0)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlistener(entry.update, entry.context);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tif (subscriber.terminated) subscriber.closed = true;\n\t\t\t\t}\n\t\t\t\tthrowCollectedErrors(errors, \"Failed to activate remote service subscription\");\n\t\t\t},\n\t\t\tclose: () => {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tsubscriber.closed = true;\n\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\tregistration.subscribers.delete(subscriber);\n\t\t\t},\n\t\t};\n\t}\n\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\tconst errors: unknown[] = [];\n\t\tfor (const registration of this.#registrations.values()) {\n\t\t\tconst singleton = registration.singleton;\n\t\t\tif (singleton !== undefined) {\n\t\t\t\tsingleton.active = false;\n\t\t\t\tfor (const remove of singleton.removeMemberListeners) remove();\n\t\t\t\tdelete registration.singleton;\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const [key, instance] of [...registration.instances]) {\n\t\t\t\tinstance.active = false;\n\t\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\t\tregistration.instances.delete(key);\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"closed\", instance: instance.address! });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const subscriber of registration.subscribers) {\n\t\t\t\tif (subscriber.active) {\n\t\t\t\t\tsubscriber.closed = true;\n\t\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\t} else {\n\t\t\t\t\tsubscriber.terminated = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tregistration.subscribers.clear();\n\t\t}\n\t\tthis.#registrations.clear();\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose remote service provider\");\n\t}\n\n\t#registration(serviceId: string, mode: ServiceMode): ServiceRegistration {\n\t\tconst registration = this.#registrations.get(serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${serviceId}`);\n\t\t}\n\t\tif (registration.mode !== mode) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${serviceId} is ${registration.mode}, not ${mode}`,\n\t\t\t);\n\t\t}\n\t\treturn registration;\n\t}\n\n\t#createInstance(\n\t\tregistration: ServiceRegistration,\n\t\tclassified: ClassifiedRemoteServiceImplementation,\n\t\taddress: ServiceInstanceAddress | undefined,\n\t): ProviderInstance {\n\t\tconst removeMemberListeners: (() => void)[] = [];\n\t\tconst instance: ProviderInstance = {\n\t\t\t...(address === undefined ? {} : { address }),\n\t\t\timplementation: classified.implementation,\n\t\t\tmembers: classified.members,\n\t\t\tremoveMemberListeners,\n\t\t\tactive: true,\n\t\t};\n\t\tfor (const [name, member] of classified.members) {\n\t\t\tif (member.kind !== \"state\") continue;\n\t\t\tremoveMemberListeners.push(\n\t\t\t\tmember.state.subscribe((ops, sequence, context) => {\n\t\t\t\t\tif (!instance.active) return;\n\t\t\t\t\tthis.#emit(\n\t\t\t\t\t\tregistration,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"state\",\n\t\t\t\t\t\t\t...(address === undefined ? {} : { instance: address }),\n\t\t\t\t\t\t\tmember: name,\n\t\t\t\t\t\t\tsequence,\n\t\t\t\t\t\t\tops,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#assertSingletonShape(registration: ServiceRegistration, replacement: ReadonlyMap<string, ServiceMemberKind>): void {\n\t\tconst current = registration.singletonShape;\n\t\tif (current === undefined || sameServiceMemberShape(current, replacement)) return;\n\t\tthrow new RemoteServiceError(\n\t\t\t\"service_member_mismatch\",\n\t\t\t`Remote service ${registration.serviceId} replacement must preserve its member shape`,\n\t\t);\n\t}\n\n\t#resolveInstance(registration: ServiceRegistration, address: ServiceInstanceAddress | undefined): ProviderInstance {\n\t\tif (registration.mode === \"singleton\") {\n\t\t\tif (address !== undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t\t`Remote service ${registration.serviceId} is singleton`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (registration.singleton === undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_not_found\",\n\t\t\t\t\t`Remote service ${registration.serviceId} has no provider`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn registration.singleton;\n\t\t}\n\t\tif (address === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${registration.serviceId} is keyed`);\n\t\t}\n\t\tconst instance = registration.instances.get(address.key);\n\t\tif (instance === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_instance_not_found\",\n\t\t\t\t`Remote service ${registration.serviceId} has no instance ${address.key}`,\n\t\t\t);\n\t\t}\n\t\tif (instance.address?.generation !== address.generation) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_stale_instance\",\n\t\t\t\t`Remote service ${registration.serviceId} instance ${address.key} is stale`,\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#snapshot(registration: ServiceRegistration): ServiceSubscriptionSnapshot {\n\t\tconst instances =\n\t\t\tregistration.mode === \"singleton\"\n\t\t\t\t? registration.singleton\n\t\t\t\t\t? [this.#snapshotInstance(registration.singleton)]\n\t\t\t\t\t: []\n\t\t\t\t: [...registration.instances.values()]\n\t\t\t\t\t\t.sort((left, right) => left.address!.key.localeCompare(right.address!.key))\n\t\t\t\t\t\t.map((instance) => this.#snapshotInstance(instance));\n\t\treturn { serviceId: registration.serviceId, mode: registration.mode, instances };\n\t}\n\n\t#snapshotInstance(instance: ProviderInstance): ServiceInstanceSnapshot {\n\t\tconst members: ServiceMemberSnapshot[] = [];\n\t\tfor (const [name, member] of instance.members) {\n\t\t\tif (member.kind === \"method\") {\n\t\t\t\tmembers.push({ name, kind: \"method\" });\n\t\t\t} else {\n\t\t\t\tmembers.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind: \"state\",\n\t\t\t\t\tsequence: member.state.sequence,\n\t\t\t\t\tops: [[\"r\", member.state.value as JsonValue]],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\t...(instance.address === undefined ? {} : { instance: instance.address }),\n\t\t\tmembers,\n\t\t};\n\t}\n\n\t#emit(registration: ServiceRegistration, update: ServiceProviderUpdate, context?: Context): void {\n\t\tif (registration.subscribers.size === 0) return;\n\t\tconst deliveryContext = context ?? serviceDeliveryContext();\n\t\tconst errors: unknown[] = [];\n\t\tfor (const subscriber of registration.subscribers) {\n\t\t\tif (subscriber.closed || updateCoveredBySnapshot(subscriber.snapshotSequences, update)) continue;\n\t\t\tconst entry = { update, context: deliveryContext };\n\t\t\tif (!subscriber.active) {\n\t\t\t\tsubscriber.buffer.push(entry);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsubscriber.listener(entry.update, entry.context);\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(error);\n\t\t\t}\n\t\t}\n\t\tthrowCollectedErrors(errors, `Failed to publish remote service ${registration.serviceId} update`);\n\t}\n\n\t#assertRemotable(service: { readonly id: string; readonly local: boolean }): void {\n\t\tif (service.local) throw new RemoteServiceError(\"service_not_allowed\", `Service ${service.id} is process-local`);\n\t}\n\n\t#assertAllowed(serviceId: string): void {\n\t\tif (!this.#registrations.has(serviceId)) {\n\t\t\tthrow new RemoteServiceError(\"service_not_allowed\", `Remote service ${serviceId} is not allowlisted`);\n\t\t}\n\t}\n\n\t#assertActive(): void {\n\t\tif (this.#disposed) throw new Error(\"Remote service provider is disposed\");\n\t}\n}\n\nfunction recordSnapshotSequences(sequences: Map<string, number>, instances: readonly ServiceInstanceSnapshot[]): void {\n\tfor (const instance of instances) {\n\t\tfor (const member of instance.members) {\n\t\t\tif (member.kind === \"state\") sequences.set(stateMemberKey(instance.instance, member.name), member.sequence);\n\t\t}\n\t}\n}\n\nfunction updateCoveredBySnapshot(sequences: Map<string, number>, update: ServiceProviderUpdate): boolean {\n\tswitch (update.type) {\n\t\tcase \"state\": {\n\t\t\tconst key = stateMemberKey(update.instance, update.member);\n\t\t\tconst sequence = sequences.get(key);\n\t\t\tif (sequence === undefined) return false;\n\t\t\tif (update.sequence <= sequence) return true;\n\t\t\tsequences.delete(key);\n\t\t\treturn false;\n\t\t}\n\t\tcase \"replaced\":\n\t\t\tsequences.clear();\n\t\t\trecordSnapshotSequences(sequences, [update.snapshot]);\n\t\t\treturn false;\n\t\tcase \"spawned\":\n\t\t\trecordSnapshotSequences(sequences, [update.instance]);\n\t\t\treturn false;\n\t\tcase \"unavailable\":\n\t\t\tsequences.clear();\n\t\t\treturn false;\n\t\tcase \"closed\": {\n\t\t\tconst prefix = `${JSON.stringify([update.instance.key, update.instance.generation]).slice(0, -1)},`;\n\t\t\tfor (const key of sequences.keys()) {\n\t\t\t\tif (key.startsWith(prefix)) sequences.delete(key);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction stateMemberKey(instance: ServiceInstanceAddress | undefined, member: string): string {\n\treturn JSON.stringify(instance === undefined ? [member] : [instance.key, instance.generation, member]);\n}\n\nexport function createRemoteServiceEndpoint(provider: RemoteServiceProvider): RemoteServiceEndpoint {\n\tconst subscriptions = new Map<string, ServiceSubscription>();\n\tlet disposed = false;\n\n\treturn {\n\t\tasync invoke(call, publish, context) {\n\t\t\tif (disposed) throw new Error(\"Remote service endpoint is disposed\");\n\t\t\tconst control = decodeServiceControlCall(call);\n\t\t\tif (control?.type === \"catalogue\") return provider.catalogue as unknown as JsonValue;\n\t\t\tif (control?.type === \"subscribe\") {\n\t\t\t\tif (subscriptions.has(control.subscriptionId)) {\n\t\t\t\t\tthrow new Error(\"Service subscription ID is already active\");\n\t\t\t\t}\n\t\t\t\tconst subscription = provider.subscribe(control.serviceId, control.mode, (update, updateContext) => {\n\t\t\t\t\tvoid Promise.resolve(publish(control.subscriptionId, update, updateContext)).catch(() => {});\n\t\t\t\t});\n\t\t\t\tsubscriptions.set(control.subscriptionId, subscription);\n\t\t\t\tsubscription.activate();\n\t\t\t\treturn subscription.snapshot as unknown as JsonValue;\n\t\t\t}\n\t\t\tif (control?.type === \"unsubscribe\") {\n\t\t\t\tconst subscription = subscriptions.get(control.subscriptionId);\n\t\t\t\tif (subscription === undefined) throw new Error(\"Service subscription was not found\");\n\t\t\t\tsubscription.close();\n\t\t\t\tsubscriptions.delete(control.subscriptionId);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn provider.invoke(call, context);\n\t\t},\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tfor (const subscription of subscriptions.values()) subscription.close();\n\t\t\tsubscriptions.clear();\n\t\t},\n\t};\n}\n\nexport function validateRemoteServiceImplementation(serviceId: string, implementation: unknown): void {\n\tvoid classifyRemoteServiceImplementation(serviceId, implementation);\n}\n\nfunction classifyRemoteServiceImplementation(\n\tserviceId: string,\n\timplementation: unknown,\n): ClassifiedRemoteServiceImplementation {\n\tif (typeof implementation !== \"object\" || implementation === null || Array.isArray(implementation)) {\n\t\tthrow new TypeError(`Remote service ${serviceId} implementation must be an object`);\n\t}\n\tconst members = new Map<string, InstanceMember>();\n\tfor (const name of Object.keys(implementation).sort()) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(implementation, name);\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} must be a data property`);\n\t\t}\n\t\tif (typeof descriptor.value === \"function\") {\n\t\t\tmembers.set(name, { kind: \"method\", method: descriptor.value as RemoteMethod });\n\t\t\tcontinue;\n\t\t}\n\t\tconst state = getReplicatedStateInternals(descriptor.value);\n\t\tif (state !== undefined) {\n\t\t\tmembers.set(name, { kind: \"state\", state });\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} is not remotely exposable`);\n\t}\n\tif (members.size === 0) throw new TypeError(`Remote service ${serviceId} has no members`);\n\treturn { implementation, members };\n}\n\nfunction throwCollectedErrors(errors: readonly unknown[], message: string): void {\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, message);\n}\n\nfunction serviceMemberShape(members: ReadonlyMap<string, InstanceMember>): ReadonlyMap<string, ServiceMemberKind> {\n\treturn new Map([...members].map(([name, member]) => [name, member.kind]));\n}\n\nfunction sameServiceMemberShape(\n\tleft: ReadonlyMap<string, ServiceMemberKind>,\n\tright: ReadonlyMap<string, ServiceMemberKind>,\n): boolean {\n\treturn left.size === right.size && [...left].every(([name, kind]) => right.get(name) === kind);\n}\n"]}
|
|
@@ -157,13 +157,14 @@ export class RemoteServiceProvider {
|
|
|
157
157
|
const subscriber = {
|
|
158
158
|
listener,
|
|
159
159
|
buffer: [],
|
|
160
|
+
snapshotSequences: new Map(),
|
|
160
161
|
active: false,
|
|
161
162
|
terminated: false,
|
|
162
163
|
closed: false,
|
|
163
164
|
};
|
|
164
|
-
this.#publishPending(registration);
|
|
165
165
|
registration.subscribers.add(subscriber);
|
|
166
166
|
const snapshot = this.#snapshot(registration);
|
|
167
|
+
recordSnapshotSequences(subscriber.snapshotSequences, snapshot.instances);
|
|
167
168
|
return {
|
|
168
169
|
snapshot,
|
|
169
170
|
activate: () => {
|
|
@@ -308,20 +309,6 @@ export class RemoteServiceProvider {
|
|
|
308
309
|
}
|
|
309
310
|
return instance;
|
|
310
311
|
}
|
|
311
|
-
#publishPending(registration) {
|
|
312
|
-
const context = serviceDeliveryContext();
|
|
313
|
-
const instances = registration.mode === "singleton"
|
|
314
|
-
? registration.singleton === undefined
|
|
315
|
-
? []
|
|
316
|
-
: [registration.singleton]
|
|
317
|
-
: registration.instances.values();
|
|
318
|
-
for (const instance of instances) {
|
|
319
|
-
for (const member of instance.members.values()) {
|
|
320
|
-
if (member.kind === "state")
|
|
321
|
-
member.state.publish(context);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
312
|
#snapshot(registration) {
|
|
326
313
|
const instances = registration.mode === "singleton"
|
|
327
314
|
? registration.singleton
|
|
@@ -358,7 +345,7 @@ export class RemoteServiceProvider {
|
|
|
358
345
|
const deliveryContext = context ?? serviceDeliveryContext();
|
|
359
346
|
const errors = [];
|
|
360
347
|
for (const subscriber of registration.subscribers) {
|
|
361
|
-
if (subscriber.closed)
|
|
348
|
+
if (subscriber.closed || updateCoveredBySnapshot(subscriber.snapshotSequences, update))
|
|
362
349
|
continue;
|
|
363
350
|
const entry = { update, context: deliveryContext };
|
|
364
351
|
if (!subscriber.active) {
|
|
@@ -388,6 +375,49 @@ export class RemoteServiceProvider {
|
|
|
388
375
|
throw new Error("Remote service provider is disposed");
|
|
389
376
|
}
|
|
390
377
|
}
|
|
378
|
+
function recordSnapshotSequences(sequences, instances) {
|
|
379
|
+
for (const instance of instances) {
|
|
380
|
+
for (const member of instance.members) {
|
|
381
|
+
if (member.kind === "state")
|
|
382
|
+
sequences.set(stateMemberKey(instance.instance, member.name), member.sequence);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function updateCoveredBySnapshot(sequences, update) {
|
|
387
|
+
switch (update.type) {
|
|
388
|
+
case "state": {
|
|
389
|
+
const key = stateMemberKey(update.instance, update.member);
|
|
390
|
+
const sequence = sequences.get(key);
|
|
391
|
+
if (sequence === undefined)
|
|
392
|
+
return false;
|
|
393
|
+
if (update.sequence <= sequence)
|
|
394
|
+
return true;
|
|
395
|
+
sequences.delete(key);
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
case "replaced":
|
|
399
|
+
sequences.clear();
|
|
400
|
+
recordSnapshotSequences(sequences, [update.snapshot]);
|
|
401
|
+
return false;
|
|
402
|
+
case "spawned":
|
|
403
|
+
recordSnapshotSequences(sequences, [update.instance]);
|
|
404
|
+
return false;
|
|
405
|
+
case "unavailable":
|
|
406
|
+
sequences.clear();
|
|
407
|
+
return false;
|
|
408
|
+
case "closed": {
|
|
409
|
+
const prefix = `${JSON.stringify([update.instance.key, update.instance.generation]).slice(0, -1)},`;
|
|
410
|
+
for (const key of sequences.keys()) {
|
|
411
|
+
if (key.startsWith(prefix))
|
|
412
|
+
sequences.delete(key);
|
|
413
|
+
}
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function stateMemberKey(instance, member) {
|
|
419
|
+
return JSON.stringify(instance === undefined ? [member] : [instance.key, instance.generation, member]);
|
|
420
|
+
}
|
|
391
421
|
export function createRemoteServiceEndpoint(provider) {
|
|
392
422
|
const subscriptions = new Map();
|
|
393
423
|
let disposed = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/services/provider.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tContext,\n\tJsonValue,\n\tRemoteServiceContract,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"../types.ts\";\nimport { RemoteServiceError } from \"./errors.ts\";\nimport { serviceDeliveryContext } from \"./state.ts\";\nimport { getReplicatedStateInternals, type ReplicatedStateInternals } from \"./state-internals.ts\";\nimport { decodeServiceControlCall } from \"./wire.ts\";\n\ntype RemoteMethod = (...args: unknown[]) => unknown;\n\ntype InstanceMember =\n\t| { readonly kind: \"method\"; readonly method: RemoteMethod }\n\t| { readonly kind: \"state\"; readonly state: ReplicatedStateInternals };\n\ntype ServiceMemberKind = InstanceMember[\"kind\"];\n\ninterface ClassifiedRemoteServiceImplementation {\n\treadonly implementation: object;\n\treadonly members: Map<string, InstanceMember>;\n}\n\ninterface ProviderInstance {\n\treadonly address?: ServiceInstanceAddress;\n\treadonly implementation: object;\n\treadonly members: ReadonlyMap<string, InstanceMember>;\n\treadonly removeMemberListeners: readonly (() => void)[];\n\tactive: boolean;\n}\n\ninterface ProviderSubscriber {\n\treadonly listener: (update: ServiceProviderUpdate, context: Context) => void;\n\treadonly buffer: { readonly update: ServiceProviderUpdate; readonly context: Context }[];\n\tactive: boolean;\n\tterminated: boolean;\n\tclosed: boolean;\n}\n\ninterface ServiceProviderDefinition {\n\treadonly service: { readonly id: string; readonly local?: boolean };\n\treadonly mode: ServiceMode;\n}\n\ninterface ServiceRegistration {\n\treadonly serviceId: string;\n\treadonly mode: ServiceMode;\n\tsingleton?: ProviderInstance;\n\tsingletonShape?: ReadonlyMap<string, ServiceMemberKind>;\n\treadonly instances: Map<string, ProviderInstance>;\n\treadonly generations: Map<string, number>;\n\treadonly subscribers: Set<ProviderSubscriber>;\n}\n\nexport type ServiceUpdatePublisher = (\n\tsubscriptionId: string,\n\tupdate: ServiceProviderUpdate,\n\tcontext: Context,\n) => void | Promise<void>;\n\n/** Hosts one provider for one remote consumer and owns that consumer's subscriptions. */\nexport interface RemoteServiceEndpoint {\n\tinvoke(call: ServiceCall, publish: ServiceUpdatePublisher, context: Context): Promise<JsonValue | undefined>;\n\tdispose(): void;\n}\n\nexport class RemoteServiceProvider {\n\treadonly #catalogue: readonly ServiceCatalogueEntry[];\n\treadonly #registrations = new Map<string, ServiceRegistration>();\n\t#disposed = false;\n\n\tconstructor(entries: readonly (ServiceProviderDefinition | { readonly id: string })[]) {\n\t\tconst definitions = entries.map(\n\t\t\t(entry): ServiceProviderDefinition => (\"service\" in entry ? entry : { service: entry, mode: \"singleton\" }),\n\t\t);\n\t\tfor (const { service } of definitions) {\n\t\t\tif (\"local\" in service && service.local === true) {\n\t\t\t\tthrow new TypeError(`Local service ${service.id} cannot be published remotely`);\n\t\t\t}\n\t\t}\n\t\tconst ids = definitions.map(({ service }) => service.id);\n\t\tif (new Set(ids).size !== ids.length) throw new TypeError(\"Remote service catalogue contains duplicate IDs\");\n\t\tthis.#catalogue = Object.freeze(\n\t\t\tdefinitions.map(({ service, mode }) => Object.freeze({ serviceId: service.id, mode })),\n\t\t);\n\t\tfor (const { service, mode } of definitions) {\n\t\t\tthis.#registrations.set(service.id, {\n\t\t\t\tserviceId: service.id,\n\t\t\t\tmode,\n\t\t\t\tinstances: new Map(),\n\t\t\t\tgenerations: new Map(),\n\t\t\t\tsubscribers: new Set(),\n\t\t\t});\n\t\t}\n\t}\n\n\tget catalogue(): readonly ServiceCatalogueEntry[] {\n\t\treturn this.#catalogue;\n\t}\n\n\tprovide<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tif (registration.singleton !== undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${service.id} already has a provider`);\n\t\t}\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tregistration.singleton = this.#createInstance(registration, classified, undefined);\n\t\tregistration.singletonShape = shape;\n\t}\n\n\t/** Disconnect one singleton while preserving active subscriptions and remote facades. */\n\twithdraw<T>(service: Service<T>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst previous = registration.singleton;\n\t\tif (previous === undefined) return;\n\t\tprevious.active = false;\n\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\tdelete registration.singleton;\n\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t}\n\n\t/** Check a singleton replacement without changing the active provider. */\n\tvalidateReplacement<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tthis.#assertSingletonShape(registration, serviceMemberShape(classified.members));\n\t}\n\n\t/** Replace one singleton without making its stable remote facade unavailable. */\n\treplace<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tconst replacement = this.#createInstance(registration, classified, undefined);\n\t\tconst previous = registration.singleton;\n\t\tif (previous !== undefined) {\n\t\t\tprevious.active = false;\n\t\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\t}\n\t\tregistration.singleton = replacement;\n\t\tregistration.singletonShape = shape;\n\t\tthis.#emit(registration, { type: \"replaced\", snapshot: this.#snapshotInstance(replacement) });\n\t}\n\n\tuse<T>(service: Service<T>): T {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registrations.get(service.id);\n\t\tif (registration?.mode !== \"singleton\" || registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${service.id} has no local provider`);\n\t\t}\n\t\treturn registration.singleton.implementation as T;\n\t}\n\n\tspawn<T>(service: Service<T>, key: string, implementation: NoInfer<RemoteServiceContract<T>>): () => void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tif (key.length === 0) throw new TypeError(\"Remote service instance key must not be empty\");\n\t\tconst registration = this.#registration(service.id, \"keyed\");\n\t\tif (registration.instances.has(key)) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${service.id} already has a live instance with key ${key}`,\n\t\t\t);\n\t\t}\n\t\tconst generation = (registration.generations.get(key) ?? 0) + 1;\n\t\tregistration.generations.set(key, generation);\n\t\tconst address = { key, generation } satisfies ServiceInstanceAddress;\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst instance = this.#createInstance(registration, classified, address);\n\t\tregistration.instances.set(key, instance);\n\t\tthis.#emit(registration, { type: \"spawned\", instance: this.#snapshotInstance(instance) });\n\t\tlet closed = false;\n\t\treturn () => {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tif (registration.instances.get(key) !== instance) return;\n\t\t\tinstance.active = false;\n\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\tregistration.instances.delete(key);\n\t\t\tthis.#emit(registration, { type: \"closed\", instance: address });\n\t\t};\n\t}\n\n\tasync invoke(call: ServiceCall, context: Context): Promise<JsonValue | undefined> {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(call.serviceId);\n\t\tconst registration = this.#registrations.get(call.serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${call.serviceId}`);\n\t\t}\n\t\tconst instance = this.#resolveInstance(registration, call.instance);\n\t\tconst member = instance.members.get(call.member);\n\t\tif (member === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_not_found\",\n\t\t\t\t`Unknown remote service member ${call.serviceId}.${call.member}`,\n\t\t\t);\n\t\t}\n\t\tif (member.kind !== \"method\") {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_mismatch\",\n\t\t\t\t`Remote service member ${call.serviceId}.${call.member} is not a method`,\n\t\t\t);\n\t\t}\n\t\tconst result: unknown = await Reflect.apply(member.method, instance.implementation, [...call.args, context]);\n\t\treturn result as JsonValue | undefined;\n\t}\n\n\tsubscribe(\n\t\tserviceId: string,\n\t\tmode: ServiceMode,\n\t\tlistener: (update: ServiceProviderUpdate, context: Context) => void,\n\t): ServiceSubscription {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(serviceId);\n\t\tconst registration = this.#registration(serviceId, mode);\n\t\tif (registration.mode === \"singleton\" && registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${serviceId} has no provider`);\n\t\t}\n\t\tconst subscriber: ProviderSubscriber = {\n\t\t\tlistener,\n\t\t\tbuffer: [],\n\t\t\tactive: false,\n\t\t\tterminated: false,\n\t\t\tclosed: false,\n\t\t};\n\t\tthis.#publishPending(registration);\n\t\tregistration.subscribers.add(subscriber);\n\t\tconst snapshot = this.#snapshot(registration);\n\t\treturn {\n\t\t\tsnapshot,\n\t\t\tactivate: () => {\n\t\t\t\tif (subscriber.closed || subscriber.active) return;\n\t\t\t\tsubscriber.active = true;\n\t\t\t\tconst errors: unknown[] = [];\n\t\t\t\ttry {\n\t\t\t\t\tfor (const entry of subscriber.buffer.splice(0)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlistener(entry.update, entry.context);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tif (subscriber.terminated) subscriber.closed = true;\n\t\t\t\t}\n\t\t\t\tthrowCollectedErrors(errors, \"Failed to activate remote service subscription\");\n\t\t\t},\n\t\t\tclose: () => {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tsubscriber.closed = true;\n\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\tregistration.subscribers.delete(subscriber);\n\t\t\t},\n\t\t};\n\t}\n\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\tconst errors: unknown[] = [];\n\t\tfor (const registration of this.#registrations.values()) {\n\t\t\tconst singleton = registration.singleton;\n\t\t\tif (singleton !== undefined) {\n\t\t\t\tsingleton.active = false;\n\t\t\t\tfor (const remove of singleton.removeMemberListeners) remove();\n\t\t\t\tdelete registration.singleton;\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const [key, instance] of [...registration.instances]) {\n\t\t\t\tinstance.active = false;\n\t\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\t\tregistration.instances.delete(key);\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"closed\", instance: instance.address! });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const subscriber of registration.subscribers) {\n\t\t\t\tif (subscriber.active) {\n\t\t\t\t\tsubscriber.closed = true;\n\t\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\t} else {\n\t\t\t\t\tsubscriber.terminated = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tregistration.subscribers.clear();\n\t\t}\n\t\tthis.#registrations.clear();\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose remote service provider\");\n\t}\n\n\t#registration(serviceId: string, mode: ServiceMode): ServiceRegistration {\n\t\tconst registration = this.#registrations.get(serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${serviceId}`);\n\t\t}\n\t\tif (registration.mode !== mode) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${serviceId} is ${registration.mode}, not ${mode}`,\n\t\t\t);\n\t\t}\n\t\treturn registration;\n\t}\n\n\t#createInstance(\n\t\tregistration: ServiceRegistration,\n\t\tclassified: ClassifiedRemoteServiceImplementation,\n\t\taddress: ServiceInstanceAddress | undefined,\n\t): ProviderInstance {\n\t\tconst removeMemberListeners: (() => void)[] = [];\n\t\tconst instance: ProviderInstance = {\n\t\t\t...(address === undefined ? {} : { address }),\n\t\t\timplementation: classified.implementation,\n\t\t\tmembers: classified.members,\n\t\t\tremoveMemberListeners,\n\t\t\tactive: true,\n\t\t};\n\t\tfor (const [name, member] of classified.members) {\n\t\t\tif (member.kind !== \"state\") continue;\n\t\t\tremoveMemberListeners.push(\n\t\t\t\tmember.state.subscribe((ops, sequence, context) => {\n\t\t\t\t\tif (!instance.active) return;\n\t\t\t\t\tthis.#emit(\n\t\t\t\t\t\tregistration,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"state\",\n\t\t\t\t\t\t\t...(address === undefined ? {} : { instance: address }),\n\t\t\t\t\t\t\tmember: name,\n\t\t\t\t\t\t\tsequence,\n\t\t\t\t\t\t\tops,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#assertSingletonShape(registration: ServiceRegistration, replacement: ReadonlyMap<string, ServiceMemberKind>): void {\n\t\tconst current = registration.singletonShape;\n\t\tif (current === undefined || sameServiceMemberShape(current, replacement)) return;\n\t\tthrow new RemoteServiceError(\n\t\t\t\"service_member_mismatch\",\n\t\t\t`Remote service ${registration.serviceId} replacement must preserve its member shape`,\n\t\t);\n\t}\n\n\t#resolveInstance(registration: ServiceRegistration, address: ServiceInstanceAddress | undefined): ProviderInstance {\n\t\tif (registration.mode === \"singleton\") {\n\t\t\tif (address !== undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t\t`Remote service ${registration.serviceId} is singleton`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (registration.singleton === undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_not_found\",\n\t\t\t\t\t`Remote service ${registration.serviceId} has no provider`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn registration.singleton;\n\t\t}\n\t\tif (address === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${registration.serviceId} is keyed`);\n\t\t}\n\t\tconst instance = registration.instances.get(address.key);\n\t\tif (instance === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_instance_not_found\",\n\t\t\t\t`Remote service ${registration.serviceId} has no instance ${address.key}`,\n\t\t\t);\n\t\t}\n\t\tif (instance.address?.generation !== address.generation) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_stale_instance\",\n\t\t\t\t`Remote service ${registration.serviceId} instance ${address.key} is stale`,\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#publishPending(registration: ServiceRegistration): void {\n\t\tconst context = serviceDeliveryContext();\n\t\tconst instances =\n\t\t\tregistration.mode === \"singleton\"\n\t\t\t\t? registration.singleton === undefined\n\t\t\t\t\t? []\n\t\t\t\t\t: [registration.singleton]\n\t\t\t\t: registration.instances.values();\n\t\tfor (const instance of instances) {\n\t\t\tfor (const member of instance.members.values()) {\n\t\t\t\tif (member.kind === \"state\") member.state.publish(context);\n\t\t\t}\n\t\t}\n\t}\n\n\t#snapshot(registration: ServiceRegistration): ServiceSubscriptionSnapshot {\n\t\tconst instances =\n\t\t\tregistration.mode === \"singleton\"\n\t\t\t\t? registration.singleton\n\t\t\t\t\t? [this.#snapshotInstance(registration.singleton)]\n\t\t\t\t\t: []\n\t\t\t\t: [...registration.instances.values()]\n\t\t\t\t\t\t.sort((left, right) => left.address!.key.localeCompare(right.address!.key))\n\t\t\t\t\t\t.map((instance) => this.#snapshotInstance(instance));\n\t\treturn { serviceId: registration.serviceId, mode: registration.mode, instances };\n\t}\n\n\t#snapshotInstance(instance: ProviderInstance): ServiceInstanceSnapshot {\n\t\tconst members: ServiceMemberSnapshot[] = [];\n\t\tfor (const [name, member] of instance.members) {\n\t\t\tif (member.kind === \"method\") {\n\t\t\t\tmembers.push({ name, kind: \"method\" });\n\t\t\t} else {\n\t\t\t\tmembers.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind: \"state\",\n\t\t\t\t\tsequence: member.state.sequence,\n\t\t\t\t\tops: [[\"r\", member.state.value as JsonValue]],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\t...(instance.address === undefined ? {} : { instance: instance.address }),\n\t\t\tmembers,\n\t\t};\n\t}\n\n\t#emit(registration: ServiceRegistration, update: ServiceProviderUpdate, context?: Context): void {\n\t\tif (registration.subscribers.size === 0) return;\n\t\tconst deliveryContext = context ?? serviceDeliveryContext();\n\t\tconst errors: unknown[] = [];\n\t\tfor (const subscriber of registration.subscribers) {\n\t\t\tif (subscriber.closed) continue;\n\t\t\tconst entry = { update, context: deliveryContext };\n\t\t\tif (!subscriber.active) {\n\t\t\t\tsubscriber.buffer.push(entry);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsubscriber.listener(entry.update, entry.context);\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(error);\n\t\t\t}\n\t\t}\n\t\tthrowCollectedErrors(errors, `Failed to publish remote service ${registration.serviceId} update`);\n\t}\n\n\t#assertRemotable(service: { readonly id: string; readonly local: boolean }): void {\n\t\tif (service.local) throw new RemoteServiceError(\"service_not_allowed\", `Service ${service.id} is process-local`);\n\t}\n\n\t#assertAllowed(serviceId: string): void {\n\t\tif (!this.#registrations.has(serviceId)) {\n\t\t\tthrow new RemoteServiceError(\"service_not_allowed\", `Remote service ${serviceId} is not allowlisted`);\n\t\t}\n\t}\n\n\t#assertActive(): void {\n\t\tif (this.#disposed) throw new Error(\"Remote service provider is disposed\");\n\t}\n}\n\nexport function createRemoteServiceEndpoint(provider: RemoteServiceProvider): RemoteServiceEndpoint {\n\tconst subscriptions = new Map<string, ServiceSubscription>();\n\tlet disposed = false;\n\n\treturn {\n\t\tasync invoke(call, publish, context) {\n\t\t\tif (disposed) throw new Error(\"Remote service endpoint is disposed\");\n\t\t\tconst control = decodeServiceControlCall(call);\n\t\t\tif (control?.type === \"catalogue\") return provider.catalogue as unknown as JsonValue;\n\t\t\tif (control?.type === \"subscribe\") {\n\t\t\t\tif (subscriptions.has(control.subscriptionId)) {\n\t\t\t\t\tthrow new Error(\"Service subscription ID is already active\");\n\t\t\t\t}\n\t\t\t\tconst subscription = provider.subscribe(control.serviceId, control.mode, (update, updateContext) => {\n\t\t\t\t\tvoid Promise.resolve(publish(control.subscriptionId, update, updateContext)).catch(() => {});\n\t\t\t\t});\n\t\t\t\tsubscriptions.set(control.subscriptionId, subscription);\n\t\t\t\tsubscription.activate();\n\t\t\t\treturn subscription.snapshot as unknown as JsonValue;\n\t\t\t}\n\t\t\tif (control?.type === \"unsubscribe\") {\n\t\t\t\tconst subscription = subscriptions.get(control.subscriptionId);\n\t\t\t\tif (subscription === undefined) throw new Error(\"Service subscription was not found\");\n\t\t\t\tsubscription.close();\n\t\t\t\tsubscriptions.delete(control.subscriptionId);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn provider.invoke(call, context);\n\t\t},\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tfor (const subscription of subscriptions.values()) subscription.close();\n\t\t\tsubscriptions.clear();\n\t\t},\n\t};\n}\n\nexport function validateRemoteServiceImplementation(serviceId: string, implementation: unknown): void {\n\tvoid classifyRemoteServiceImplementation(serviceId, implementation);\n}\n\nfunction classifyRemoteServiceImplementation(\n\tserviceId: string,\n\timplementation: unknown,\n): ClassifiedRemoteServiceImplementation {\n\tif (typeof implementation !== \"object\" || implementation === null || Array.isArray(implementation)) {\n\t\tthrow new TypeError(`Remote service ${serviceId} implementation must be an object`);\n\t}\n\tconst members = new Map<string, InstanceMember>();\n\tfor (const name of Object.keys(implementation).sort()) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(implementation, name);\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} must be a data property`);\n\t\t}\n\t\tif (typeof descriptor.value === \"function\") {\n\t\t\tmembers.set(name, { kind: \"method\", method: descriptor.value as RemoteMethod });\n\t\t\tcontinue;\n\t\t}\n\t\tconst state = getReplicatedStateInternals(descriptor.value);\n\t\tif (state !== undefined) {\n\t\t\tmembers.set(name, { kind: \"state\", state });\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} is not remotely exposable`);\n\t}\n\tif (members.size === 0) throw new TypeError(`Remote service ${serviceId} has no members`);\n\treturn { implementation, members };\n}\n\nfunction throwCollectedErrors(errors: readonly unknown[], message: string): void {\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, message);\n}\n\nfunction serviceMemberShape(members: ReadonlyMap<string, InstanceMember>): ReadonlyMap<string, ServiceMemberKind> {\n\treturn new Map([...members].map(([name, member]) => [name, member.kind]));\n}\n\nfunction sameServiceMemberShape(\n\tleft: ReadonlyMap<string, ServiceMemberKind>,\n\tright: ReadonlyMap<string, ServiceMemberKind>,\n): boolean {\n\treturn left.size === right.size && [...left].every(([name, kind]) => right.get(name) === kind);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/services/provider.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tContext,\n\tJsonValue,\n\tRemoteServiceContract,\n\tService,\n\tServiceCall,\n\tServiceCatalogueEntry,\n\tServiceInstanceAddress,\n\tServiceInstanceSnapshot,\n\tServiceMemberSnapshot,\n\tServiceMode,\n\tServiceProviderUpdate,\n\tServiceSubscription,\n\tServiceSubscriptionSnapshot,\n} from \"../types.ts\";\nimport { RemoteServiceError } from \"./errors.ts\";\nimport { serviceDeliveryContext } from \"./state.ts\";\nimport { getReplicatedStateInternals, type ReplicatedStateInternals } from \"./state-internals.ts\";\nimport { decodeServiceControlCall } from \"./wire.ts\";\n\ntype RemoteMethod = (...args: unknown[]) => unknown;\n\ntype InstanceMember =\n\t| { readonly kind: \"method\"; readonly method: RemoteMethod }\n\t| { readonly kind: \"state\"; readonly state: ReplicatedStateInternals };\n\ntype ServiceMemberKind = InstanceMember[\"kind\"];\n\ninterface ClassifiedRemoteServiceImplementation {\n\treadonly implementation: object;\n\treadonly members: Map<string, InstanceMember>;\n}\n\ninterface ProviderInstance {\n\treadonly address?: ServiceInstanceAddress;\n\treadonly implementation: object;\n\treadonly members: ReadonlyMap<string, InstanceMember>;\n\treadonly removeMemberListeners: readonly (() => void)[];\n\tactive: boolean;\n}\n\ninterface ProviderSubscriber {\n\treadonly listener: (update: ServiceProviderUpdate, context: Context) => void;\n\treadonly buffer: { readonly update: ServiceProviderUpdate; readonly context: Context }[];\n\treadonly snapshotSequences: Map<string, number>;\n\tactive: boolean;\n\tterminated: boolean;\n\tclosed: boolean;\n}\n\ninterface ServiceProviderDefinition {\n\treadonly service: { readonly id: string; readonly local?: boolean };\n\treadonly mode: ServiceMode;\n}\n\ninterface ServiceRegistration {\n\treadonly serviceId: string;\n\treadonly mode: ServiceMode;\n\tsingleton?: ProviderInstance;\n\tsingletonShape?: ReadonlyMap<string, ServiceMemberKind>;\n\treadonly instances: Map<string, ProviderInstance>;\n\treadonly generations: Map<string, number>;\n\treadonly subscribers: Set<ProviderSubscriber>;\n}\n\nexport type ServiceUpdatePublisher = (\n\tsubscriptionId: string,\n\tupdate: ServiceProviderUpdate,\n\tcontext: Context,\n) => void | Promise<void>;\n\n/** Hosts one provider for one remote consumer and owns that consumer's subscriptions. */\nexport interface RemoteServiceEndpoint {\n\tinvoke(call: ServiceCall, publish: ServiceUpdatePublisher, context: Context): Promise<JsonValue | undefined>;\n\tdispose(): void;\n}\n\nexport class RemoteServiceProvider {\n\treadonly #catalogue: readonly ServiceCatalogueEntry[];\n\treadonly #registrations = new Map<string, ServiceRegistration>();\n\t#disposed = false;\n\n\tconstructor(entries: readonly (ServiceProviderDefinition | { readonly id: string })[]) {\n\t\tconst definitions = entries.map(\n\t\t\t(entry): ServiceProviderDefinition => (\"service\" in entry ? entry : { service: entry, mode: \"singleton\" }),\n\t\t);\n\t\tfor (const { service } of definitions) {\n\t\t\tif (\"local\" in service && service.local === true) {\n\t\t\t\tthrow new TypeError(`Local service ${service.id} cannot be published remotely`);\n\t\t\t}\n\t\t}\n\t\tconst ids = definitions.map(({ service }) => service.id);\n\t\tif (new Set(ids).size !== ids.length) throw new TypeError(\"Remote service catalogue contains duplicate IDs\");\n\t\tthis.#catalogue = Object.freeze(\n\t\t\tdefinitions.map(({ service, mode }) => Object.freeze({ serviceId: service.id, mode })),\n\t\t);\n\t\tfor (const { service, mode } of definitions) {\n\t\t\tthis.#registrations.set(service.id, {\n\t\t\t\tserviceId: service.id,\n\t\t\t\tmode,\n\t\t\t\tinstances: new Map(),\n\t\t\t\tgenerations: new Map(),\n\t\t\t\tsubscribers: new Set(),\n\t\t\t});\n\t\t}\n\t}\n\n\tget catalogue(): readonly ServiceCatalogueEntry[] {\n\t\treturn this.#catalogue;\n\t}\n\n\tprovide<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tif (registration.singleton !== undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${service.id} already has a provider`);\n\t\t}\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tregistration.singleton = this.#createInstance(registration, classified, undefined);\n\t\tregistration.singletonShape = shape;\n\t}\n\n\t/** Disconnect one singleton while preserving active subscriptions and remote facades. */\n\twithdraw<T>(service: Service<T>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst previous = registration.singleton;\n\t\tif (previous === undefined) return;\n\t\tprevious.active = false;\n\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\tdelete registration.singleton;\n\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t}\n\n\t/** Check a singleton replacement without changing the active provider. */\n\tvalidateReplacement<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tthis.#assertSingletonShape(registration, serviceMemberShape(classified.members));\n\t}\n\n\t/** Replace one singleton without making its stable remote facade unavailable. */\n\treplace<T>(service: Service<T>, implementation: NoInfer<RemoteServiceContract<T>>): void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registration(service.id, \"singleton\");\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst shape = serviceMemberShape(classified.members);\n\t\tthis.#assertSingletonShape(registration, shape);\n\t\tconst replacement = this.#createInstance(registration, classified, undefined);\n\t\tconst previous = registration.singleton;\n\t\tif (previous !== undefined) {\n\t\t\tprevious.active = false;\n\t\t\tfor (const remove of previous.removeMemberListeners) remove();\n\t\t}\n\t\tregistration.singleton = replacement;\n\t\tregistration.singletonShape = shape;\n\t\tthis.#emit(registration, { type: \"replaced\", snapshot: this.#snapshotInstance(replacement) });\n\t}\n\n\tuse<T>(service: Service<T>): T {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tconst registration = this.#registrations.get(service.id);\n\t\tif (registration?.mode !== \"singleton\" || registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${service.id} has no local provider`);\n\t\t}\n\t\treturn registration.singleton.implementation as T;\n\t}\n\n\tspawn<T>(service: Service<T>, key: string, implementation: NoInfer<RemoteServiceContract<T>>): () => void {\n\t\tthis.#assertActive();\n\t\tthis.#assertRemotable(service);\n\t\tthis.#assertAllowed(service.id);\n\t\tif (key.length === 0) throw new TypeError(\"Remote service instance key must not be empty\");\n\t\tconst registration = this.#registration(service.id, \"keyed\");\n\t\tif (registration.instances.has(key)) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${service.id} already has a live instance with key ${key}`,\n\t\t\t);\n\t\t}\n\t\tconst generation = (registration.generations.get(key) ?? 0) + 1;\n\t\tregistration.generations.set(key, generation);\n\t\tconst address = { key, generation } satisfies ServiceInstanceAddress;\n\t\tconst classified = classifyRemoteServiceImplementation(registration.serviceId, implementation);\n\t\tconst instance = this.#createInstance(registration, classified, address);\n\t\tregistration.instances.set(key, instance);\n\t\tthis.#emit(registration, { type: \"spawned\", instance: this.#snapshotInstance(instance) });\n\t\tlet closed = false;\n\t\treturn () => {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tif (registration.instances.get(key) !== instance) return;\n\t\t\tinstance.active = false;\n\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\tregistration.instances.delete(key);\n\t\t\tthis.#emit(registration, { type: \"closed\", instance: address });\n\t\t};\n\t}\n\n\tasync invoke(call: ServiceCall, context: Context): Promise<JsonValue | undefined> {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(call.serviceId);\n\t\tconst registration = this.#registrations.get(call.serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${call.serviceId}`);\n\t\t}\n\t\tconst instance = this.#resolveInstance(registration, call.instance);\n\t\tconst member = instance.members.get(call.member);\n\t\tif (member === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_not_found\",\n\t\t\t\t`Unknown remote service member ${call.serviceId}.${call.member}`,\n\t\t\t);\n\t\t}\n\t\tif (member.kind !== \"method\") {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_member_mismatch\",\n\t\t\t\t`Remote service member ${call.serviceId}.${call.member} is not a method`,\n\t\t\t);\n\t\t}\n\t\tconst result: unknown = await Reflect.apply(member.method, instance.implementation, [...call.args, context]);\n\t\treturn result as JsonValue | undefined;\n\t}\n\n\tsubscribe(\n\t\tserviceId: string,\n\t\tmode: ServiceMode,\n\t\tlistener: (update: ServiceProviderUpdate, context: Context) => void,\n\t): ServiceSubscription {\n\t\tthis.#assertActive();\n\t\tthis.#assertAllowed(serviceId);\n\t\tconst registration = this.#registration(serviceId, mode);\n\t\tif (registration.mode === \"singleton\" && registration.singleton === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Remote service ${serviceId} has no provider`);\n\t\t}\n\t\tconst subscriber: ProviderSubscriber = {\n\t\t\tlistener,\n\t\t\tbuffer: [],\n\t\t\tsnapshotSequences: new Map(),\n\t\t\tactive: false,\n\t\t\tterminated: false,\n\t\t\tclosed: false,\n\t\t};\n\t\tregistration.subscribers.add(subscriber);\n\t\tconst snapshot = this.#snapshot(registration);\n\t\trecordSnapshotSequences(subscriber.snapshotSequences, snapshot.instances);\n\t\treturn {\n\t\t\tsnapshot,\n\t\t\tactivate: () => {\n\t\t\t\tif (subscriber.closed || subscriber.active) return;\n\t\t\t\tsubscriber.active = true;\n\t\t\t\tconst errors: unknown[] = [];\n\t\t\t\ttry {\n\t\t\t\t\tfor (const entry of subscriber.buffer.splice(0)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tlistener(entry.update, entry.context);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tif (subscriber.terminated) subscriber.closed = true;\n\t\t\t\t}\n\t\t\t\tthrowCollectedErrors(errors, \"Failed to activate remote service subscription\");\n\t\t\t},\n\t\t\tclose: () => {\n\t\t\t\tif (subscriber.closed) return;\n\t\t\t\tsubscriber.closed = true;\n\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\tregistration.subscribers.delete(subscriber);\n\t\t\t},\n\t\t};\n\t}\n\n\tdispose(): void {\n\t\tif (this.#disposed) return;\n\t\tthis.#disposed = true;\n\t\tconst errors: unknown[] = [];\n\t\tfor (const registration of this.#registrations.values()) {\n\t\t\tconst singleton = registration.singleton;\n\t\t\tif (singleton !== undefined) {\n\t\t\t\tsingleton.active = false;\n\t\t\t\tfor (const remove of singleton.removeMemberListeners) remove();\n\t\t\t\tdelete registration.singleton;\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"unavailable\" });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const [key, instance] of [...registration.instances]) {\n\t\t\t\tinstance.active = false;\n\t\t\t\tfor (const remove of instance.removeMemberListeners) remove();\n\t\t\t\tregistration.instances.delete(key);\n\t\t\t\ttry {\n\t\t\t\t\tthis.#emit(registration, { type: \"closed\", instance: instance.address! });\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const subscriber of registration.subscribers) {\n\t\t\t\tif (subscriber.active) {\n\t\t\t\t\tsubscriber.closed = true;\n\t\t\t\t\tsubscriber.buffer.length = 0;\n\t\t\t\t} else {\n\t\t\t\t\tsubscriber.terminated = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tregistration.subscribers.clear();\n\t\t}\n\t\tthis.#registrations.clear();\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose remote service provider\");\n\t}\n\n\t#registration(serviceId: string, mode: ServiceMode): ServiceRegistration {\n\t\tconst registration = this.#registrations.get(serviceId);\n\t\tif (registration === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_not_found\", `Unknown remote service ${serviceId}`);\n\t\t}\n\t\tif (registration.mode !== mode) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t`Remote service ${serviceId} is ${registration.mode}, not ${mode}`,\n\t\t\t);\n\t\t}\n\t\treturn registration;\n\t}\n\n\t#createInstance(\n\t\tregistration: ServiceRegistration,\n\t\tclassified: ClassifiedRemoteServiceImplementation,\n\t\taddress: ServiceInstanceAddress | undefined,\n\t): ProviderInstance {\n\t\tconst removeMemberListeners: (() => void)[] = [];\n\t\tconst instance: ProviderInstance = {\n\t\t\t...(address === undefined ? {} : { address }),\n\t\t\timplementation: classified.implementation,\n\t\t\tmembers: classified.members,\n\t\t\tremoveMemberListeners,\n\t\t\tactive: true,\n\t\t};\n\t\tfor (const [name, member] of classified.members) {\n\t\t\tif (member.kind !== \"state\") continue;\n\t\t\tremoveMemberListeners.push(\n\t\t\t\tmember.state.subscribe((ops, sequence, context) => {\n\t\t\t\t\tif (!instance.active) return;\n\t\t\t\t\tthis.#emit(\n\t\t\t\t\t\tregistration,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"state\",\n\t\t\t\t\t\t\t...(address === undefined ? {} : { instance: address }),\n\t\t\t\t\t\t\tmember: name,\n\t\t\t\t\t\t\tsequence,\n\t\t\t\t\t\t\tops,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#assertSingletonShape(registration: ServiceRegistration, replacement: ReadonlyMap<string, ServiceMemberKind>): void {\n\t\tconst current = registration.singletonShape;\n\t\tif (current === undefined || sameServiceMemberShape(current, replacement)) return;\n\t\tthrow new RemoteServiceError(\n\t\t\t\"service_member_mismatch\",\n\t\t\t`Remote service ${registration.serviceId} replacement must preserve its member shape`,\n\t\t);\n\t}\n\n\t#resolveInstance(registration: ServiceRegistration, address: ServiceInstanceAddress | undefined): ProviderInstance {\n\t\tif (registration.mode === \"singleton\") {\n\t\t\tif (address !== undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_mode_mismatch\",\n\t\t\t\t\t`Remote service ${registration.serviceId} is singleton`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (registration.singleton === undefined) {\n\t\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\t\"service_not_found\",\n\t\t\t\t\t`Remote service ${registration.serviceId} has no provider`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn registration.singleton;\n\t\t}\n\t\tif (address === undefined) {\n\t\t\tthrow new RemoteServiceError(\"service_mode_mismatch\", `Remote service ${registration.serviceId} is keyed`);\n\t\t}\n\t\tconst instance = registration.instances.get(address.key);\n\t\tif (instance === undefined) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_instance_not_found\",\n\t\t\t\t`Remote service ${registration.serviceId} has no instance ${address.key}`,\n\t\t\t);\n\t\t}\n\t\tif (instance.address?.generation !== address.generation) {\n\t\t\tthrow new RemoteServiceError(\n\t\t\t\t\"service_stale_instance\",\n\t\t\t\t`Remote service ${registration.serviceId} instance ${address.key} is stale`,\n\t\t\t);\n\t\t}\n\t\treturn instance;\n\t}\n\n\t#snapshot(registration: ServiceRegistration): ServiceSubscriptionSnapshot {\n\t\tconst instances =\n\t\t\tregistration.mode === \"singleton\"\n\t\t\t\t? registration.singleton\n\t\t\t\t\t? [this.#snapshotInstance(registration.singleton)]\n\t\t\t\t\t: []\n\t\t\t\t: [...registration.instances.values()]\n\t\t\t\t\t\t.sort((left, right) => left.address!.key.localeCompare(right.address!.key))\n\t\t\t\t\t\t.map((instance) => this.#snapshotInstance(instance));\n\t\treturn { serviceId: registration.serviceId, mode: registration.mode, instances };\n\t}\n\n\t#snapshotInstance(instance: ProviderInstance): ServiceInstanceSnapshot {\n\t\tconst members: ServiceMemberSnapshot[] = [];\n\t\tfor (const [name, member] of instance.members) {\n\t\t\tif (member.kind === \"method\") {\n\t\t\t\tmembers.push({ name, kind: \"method\" });\n\t\t\t} else {\n\t\t\t\tmembers.push({\n\t\t\t\t\tname,\n\t\t\t\t\tkind: \"state\",\n\t\t\t\t\tsequence: member.state.sequence,\n\t\t\t\t\tops: [[\"r\", member.state.value as JsonValue]],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\t...(instance.address === undefined ? {} : { instance: instance.address }),\n\t\t\tmembers,\n\t\t};\n\t}\n\n\t#emit(registration: ServiceRegistration, update: ServiceProviderUpdate, context?: Context): void {\n\t\tif (registration.subscribers.size === 0) return;\n\t\tconst deliveryContext = context ?? serviceDeliveryContext();\n\t\tconst errors: unknown[] = [];\n\t\tfor (const subscriber of registration.subscribers) {\n\t\t\tif (subscriber.closed || updateCoveredBySnapshot(subscriber.snapshotSequences, update)) continue;\n\t\t\tconst entry = { update, context: deliveryContext };\n\t\t\tif (!subscriber.active) {\n\t\t\t\tsubscriber.buffer.push(entry);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsubscriber.listener(entry.update, entry.context);\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(error);\n\t\t\t}\n\t\t}\n\t\tthrowCollectedErrors(errors, `Failed to publish remote service ${registration.serviceId} update`);\n\t}\n\n\t#assertRemotable(service: { readonly id: string; readonly local: boolean }): void {\n\t\tif (service.local) throw new RemoteServiceError(\"service_not_allowed\", `Service ${service.id} is process-local`);\n\t}\n\n\t#assertAllowed(serviceId: string): void {\n\t\tif (!this.#registrations.has(serviceId)) {\n\t\t\tthrow new RemoteServiceError(\"service_not_allowed\", `Remote service ${serviceId} is not allowlisted`);\n\t\t}\n\t}\n\n\t#assertActive(): void {\n\t\tif (this.#disposed) throw new Error(\"Remote service provider is disposed\");\n\t}\n}\n\nfunction recordSnapshotSequences(sequences: Map<string, number>, instances: readonly ServiceInstanceSnapshot[]): void {\n\tfor (const instance of instances) {\n\t\tfor (const member of instance.members) {\n\t\t\tif (member.kind === \"state\") sequences.set(stateMemberKey(instance.instance, member.name), member.sequence);\n\t\t}\n\t}\n}\n\nfunction updateCoveredBySnapshot(sequences: Map<string, number>, update: ServiceProviderUpdate): boolean {\n\tswitch (update.type) {\n\t\tcase \"state\": {\n\t\t\tconst key = stateMemberKey(update.instance, update.member);\n\t\t\tconst sequence = sequences.get(key);\n\t\t\tif (sequence === undefined) return false;\n\t\t\tif (update.sequence <= sequence) return true;\n\t\t\tsequences.delete(key);\n\t\t\treturn false;\n\t\t}\n\t\tcase \"replaced\":\n\t\t\tsequences.clear();\n\t\t\trecordSnapshotSequences(sequences, [update.snapshot]);\n\t\t\treturn false;\n\t\tcase \"spawned\":\n\t\t\trecordSnapshotSequences(sequences, [update.instance]);\n\t\t\treturn false;\n\t\tcase \"unavailable\":\n\t\t\tsequences.clear();\n\t\t\treturn false;\n\t\tcase \"closed\": {\n\t\t\tconst prefix = `${JSON.stringify([update.instance.key, update.instance.generation]).slice(0, -1)},`;\n\t\t\tfor (const key of sequences.keys()) {\n\t\t\t\tif (key.startsWith(prefix)) sequences.delete(key);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nfunction stateMemberKey(instance: ServiceInstanceAddress | undefined, member: string): string {\n\treturn JSON.stringify(instance === undefined ? [member] : [instance.key, instance.generation, member]);\n}\n\nexport function createRemoteServiceEndpoint(provider: RemoteServiceProvider): RemoteServiceEndpoint {\n\tconst subscriptions = new Map<string, ServiceSubscription>();\n\tlet disposed = false;\n\n\treturn {\n\t\tasync invoke(call, publish, context) {\n\t\t\tif (disposed) throw new Error(\"Remote service endpoint is disposed\");\n\t\t\tconst control = decodeServiceControlCall(call);\n\t\t\tif (control?.type === \"catalogue\") return provider.catalogue as unknown as JsonValue;\n\t\t\tif (control?.type === \"subscribe\") {\n\t\t\t\tif (subscriptions.has(control.subscriptionId)) {\n\t\t\t\t\tthrow new Error(\"Service subscription ID is already active\");\n\t\t\t\t}\n\t\t\t\tconst subscription = provider.subscribe(control.serviceId, control.mode, (update, updateContext) => {\n\t\t\t\t\tvoid Promise.resolve(publish(control.subscriptionId, update, updateContext)).catch(() => {});\n\t\t\t\t});\n\t\t\t\tsubscriptions.set(control.subscriptionId, subscription);\n\t\t\t\tsubscription.activate();\n\t\t\t\treturn subscription.snapshot as unknown as JsonValue;\n\t\t\t}\n\t\t\tif (control?.type === \"unsubscribe\") {\n\t\t\t\tconst subscription = subscriptions.get(control.subscriptionId);\n\t\t\t\tif (subscription === undefined) throw new Error(\"Service subscription was not found\");\n\t\t\t\tsubscription.close();\n\t\t\t\tsubscriptions.delete(control.subscriptionId);\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\treturn provider.invoke(call, context);\n\t\t},\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tfor (const subscription of subscriptions.values()) subscription.close();\n\t\t\tsubscriptions.clear();\n\t\t},\n\t};\n}\n\nexport function validateRemoteServiceImplementation(serviceId: string, implementation: unknown): void {\n\tvoid classifyRemoteServiceImplementation(serviceId, implementation);\n}\n\nfunction classifyRemoteServiceImplementation(\n\tserviceId: string,\n\timplementation: unknown,\n): ClassifiedRemoteServiceImplementation {\n\tif (typeof implementation !== \"object\" || implementation === null || Array.isArray(implementation)) {\n\t\tthrow new TypeError(`Remote service ${serviceId} implementation must be an object`);\n\t}\n\tconst members = new Map<string, InstanceMember>();\n\tfor (const name of Object.keys(implementation).sort()) {\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(implementation, name);\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} must be a data property`);\n\t\t}\n\t\tif (typeof descriptor.value === \"function\") {\n\t\t\tmembers.set(name, { kind: \"method\", method: descriptor.value as RemoteMethod });\n\t\t\tcontinue;\n\t\t}\n\t\tconst state = getReplicatedStateInternals(descriptor.value);\n\t\tif (state !== undefined) {\n\t\t\tmembers.set(name, { kind: \"state\", state });\n\t\t\tcontinue;\n\t\t}\n\t\tthrow new TypeError(`Remote service member ${serviceId}.${name} is not remotely exposable`);\n\t}\n\tif (members.size === 0) throw new TypeError(`Remote service ${serviceId} has no members`);\n\treturn { implementation, members };\n}\n\nfunction throwCollectedErrors(errors: readonly unknown[], message: string): void {\n\tif (errors.length === 1) throw errors[0];\n\tif (errors.length > 1) throw new AggregateError(errors, message);\n}\n\nfunction serviceMemberShape(members: ReadonlyMap<string, InstanceMember>): ReadonlyMap<string, ServiceMemberKind> {\n\treturn new Map([...members].map(([name, member]) => [name, member.kind]));\n}\n\nfunction sameServiceMemberShape(\n\tleft: ReadonlyMap<string, ServiceMemberKind>,\n\tright: ReadonlyMap<string, ServiceMemberKind>,\n): boolean {\n\treturn left.size === right.size && [...left].every(([name, kind]) => right.get(name) === kind);\n}\n"]}
|
|
@@ -3,7 +3,6 @@ import type { Context } from "../types.ts";
|
|
|
3
3
|
export interface ReplicatedStateInternals {
|
|
4
4
|
readonly sequence: number;
|
|
5
5
|
readonly value: unknown;
|
|
6
|
-
publish(context: Context): void;
|
|
7
6
|
subscribe(listener: (ops: readonly Op[], sequence: number, context: Context) => void): () => void;
|
|
8
7
|
}
|
|
9
8
|
export declare function registerReplicatedStateInternals(value: object, internals: ReplicatedStateInternals): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-internals.d.ts","sourceRoot":"","sources":["../../src/services/state-internals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,
|
|
1
|
+
{"version":3,"file":"state-internals.d.ts","sourceRoot":"","sources":["../../src/services/state-internals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAClG;AAID,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAEzG;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,GAAG,SAAS,CAGhG","sourcesContent":["import type { Op } from \"../delta/index.ts\";\nimport type { Context } from \"../types.ts\";\n\nexport interface ReplicatedStateInternals {\n\treadonly sequence: number;\n\treadonly value: unknown;\n\tsubscribe(listener: (ops: readonly Op[], sequence: number, context: Context) => void): () => void;\n}\n\nconst sources = new WeakMap<object, ReplicatedStateInternals>();\n\nexport function registerReplicatedStateInternals(value: object, internals: ReplicatedStateInternals): void {\n\tsources.set(value, internals);\n}\n\nexport function getReplicatedStateInternals(value: unknown): ReplicatedStateInternals | undefined {\n\tif (typeof value !== \"object\" || value === null) return undefined;\n\treturn sources.get(value);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-internals.js","sourceRoot":"","sources":["../../src/services/state-internals.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"state-internals.js","sourceRoot":"","sources":["../../src/services/state-internals.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,GAAG,IAAI,OAAO,EAAoC,CAAC;AAEhE,MAAM,UAAU,gCAAgC,CAAC,KAAa,EAAE,SAAmC,EAAQ;IAC1G,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AAAA,CAC9B;AAED,MAAM,UAAU,2BAA2B,CAAC,KAAc,EAAwC;IACjG,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAAA,CAC1B","sourcesContent":["import type { Op } from \"../delta/index.ts\";\nimport type { Context } from \"../types.ts\";\n\nexport interface ReplicatedStateInternals {\n\treadonly sequence: number;\n\treadonly value: unknown;\n\tsubscribe(listener: (ops: readonly Op[], sequence: number, context: Context) => void): () => void;\n}\n\nconst sources = new WeakMap<object, ReplicatedStateInternals>();\n\nexport function registerReplicatedStateInternals(value: object, internals: ReplicatedStateInternals): void {\n\tsources.set(value, internals);\n}\n\nexport function getReplicatedStateInternals(value: unknown): ReplicatedStateInternals | undefined {\n\tif (typeof value !== \"object\" || value === null) return undefined;\n\treturn sources.get(value);\n}\n"]}
|
package/dist/services/state.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export declare class MutableReplicatedStateImpl<T extends object> implements Mut
|
|
|
4
4
|
#private;
|
|
5
5
|
constructor(initial: T);
|
|
6
6
|
get value(): T;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
change(context: Context, mutate: Parameters<MutableReplicatedState<T>["change"]>[1]): void;
|
|
8
|
+
replace(context: Context, value: T): void;
|
|
9
9
|
subscribe(listener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void): () => void;
|
|
10
10
|
}
|
|
11
11
|
/** A cold read-only state used by service consumers until a complete snapshot arrives. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/services/state.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/services/state.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,EAAE,EAAE,MAAM,mBAAmB,CAAC;AAIpE,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAUxH,qBAAa,0BAA0B,CAAC,CAAC,SAAS,MAAM,CAAE,YAAW,sBAAsB,CAAC,CAAC,CAAC;;IAU7F,YAAY,OAAO,EAAE,CAAC,EAerB;IAED,IAAI,KAAK,IAAI,CAAC,CAEb;IAED,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAYzF;IAED,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAGxC;IAED,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI,CAWvG;CAwCD;AAED,0FAA0F;AAC1F,qBAAa,sBAAsB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;;IAOjG,YAAY,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAE9C;IAED,IAAI,KAAK,IAAI,CAAC,GAAG,SAAS,CAEzB;IAED,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI,CASvG;IAED,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAYpE;IAED,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAkBnE;IAED,KAAK,IAAI,IAAI,CAGZ;CAmBD;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD","sourcesContent":["import { BACKGROUND_CONTEXT } from \"../context/index.ts\";\nimport { applyImmutable, isBase, type Op } from \"../delta/index.ts\";\nimport { diffRevisions } from \"../state/diff.ts\";\nimport { produceWithMetadata } from \"../state/draft.ts\";\nimport { JsonRevisionStore } from \"../state/value.ts\";\nimport type { Context, JsonValue, MutableReplicatedState, ReplicatedState, ReplicatedStateDelivery } from \"../types.ts\";\nimport { registerReplicatedStateInternals } from \"./state-internals.ts\";\n\ntype Publication<T> = {\n\tvalue: T;\n\tops: readonly Op[];\n\tsequence: number;\n\tcontext: Context;\n};\n\nexport class MutableReplicatedStateImpl<T extends object> implements MutableReplicatedState<T> {\n\treadonly #listeners = new Map<(value: T, context: Context, delivery: ReplicatedStateDelivery) => void, number>();\n\treadonly #sourceListeners = new Set<(ops: readonly Op[], sequence: number, context: Context) => void>();\n\treadonly #store = new JsonRevisionStore();\n\treadonly #publications: Publication<T>[] = [];\n\t#value: T;\n\t#sequence = 0;\n\t#changing = false;\n\t#delivering = false;\n\n\tconstructor(initial: T) {\n\t\tthis.#value = this.#store.import(initial);\n\t\tconst thisSource = this;\n\t\tregisterReplicatedStateInternals(this, {\n\t\t\tget sequence() {\n\t\t\t\treturn thisSource.#sequence;\n\t\t\t},\n\t\t\tget value() {\n\t\t\t\treturn thisSource.#value;\n\t\t\t},\n\t\t\tsubscribe: (listener) => {\n\t\t\t\tthisSource.#sourceListeners.add(listener);\n\t\t\t\treturn () => thisSource.#sourceListeners.delete(listener);\n\t\t\t},\n\t\t});\n\t}\n\n\tget value(): T {\n\t\treturn this.#value;\n\t}\n\n\tchange(context: Context, mutate: Parameters<MutableReplicatedState<T>[\"change\"]>[1]): void {\n\t\tif (this.#changing) throw new Error(\"Replicated state cannot be changed reentrantly from a change callback\");\n\t\tthis.#changing = true;\n\t\tlet next: T;\n\t\ttry {\n\t\t\tconst produced = produceWithMetadata(this.#value, mutate);\n\t\t\tif (produced.value === this.#value) return;\n\t\t\tnext = this.#store.commit(produced.value, produced.owned);\n\t\t} finally {\n\t\t\tthis.#changing = false;\n\t\t}\n\t\tthis.#commit(next, context);\n\t}\n\n\treplace(context: Context, value: T): void {\n\t\tif (this.#changing) throw new Error(\"Replicated state cannot be replaced from a change callback\");\n\t\tthis.#commit(this.#store.import(value), context);\n\t}\n\n\tsubscribe(listener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void): () => void {\n\t\tconst sequence = this.#sequence;\n\t\tconst value = this.#value;\n\t\tthis.#listeners.set(listener, sequence);\n\t\ttry {\n\t\t\tlistener(value, serviceDeliveryContext(), { kind: \"hydrate\", sequence });\n\t\t} catch (error) {\n\t\t\tthis.#listeners.delete(listener);\n\t\t\tthrow error;\n\t\t}\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\t#commit(next: T, context: Context): void {\n\t\tconst ops = diffRevisions(this.#value as unknown as JsonValue, next as unknown as JsonValue);\n\t\tif (ops.length === 0) return;\n\t\tthis.#value = next;\n\t\tthis.#sequence += 1;\n\t\tthis.#publications.push({ value: next, ops, sequence: this.#sequence, context });\n\t\tif (this.#delivering) return;\n\t\tthis.#delivering = true;\n\t\tconst errors: unknown[] = [];\n\t\ttry {\n\t\t\tfor (\n\t\t\t\tlet publication = this.#publications.shift();\n\t\t\t\tpublication !== undefined;\n\t\t\t\tpublication = this.#publications.shift()\n\t\t\t) {\n\t\t\t\tfor (const listener of [...this.#sourceListeners]) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlistener(publication.ops, publication.sequence, publication.context);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst delivery = { kind: \"update\", sequence: publication.sequence } as const;\n\t\t\t\tfor (const [listener, hydratedSequence] of [...this.#listeners]) {\n\t\t\t\t\tif (publication.sequence <= hydratedSequence) continue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlistener(publication.value, publication.context, delivery);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.#delivering = false;\n\t\t}\n\t\tif (errors.length === 1) throw errors[0];\n\t\tif (errors.length > 1) throw new AggregateError(errors, \"Replicated state listeners failed\");\n\t}\n}\n\n/** A cold read-only state used by service consumers until a complete snapshot arrives. */\nexport class ReplicatedStateReplica<T extends JsonValue = JsonValue> implements ReplicatedState<T> {\n\treadonly #listeners = new Set<(value: T, context: Context, delivery: ReplicatedStateDelivery) => void>();\n\treadonly #reportError: (error: Error) => void;\n\treadonly #store = new JsonRevisionStore();\n\t#value: T | undefined;\n\t#sequence: number | undefined;\n\n\tconstructor(reportError: (error: Error) => void) {\n\t\tthis.#reportError = reportError;\n\t}\n\n\tget value(): T | undefined {\n\t\treturn this.#value;\n\t}\n\n\tsubscribe(listener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void): () => void {\n\t\tthis.#listeners.add(listener);\n\t\tif (this.#value !== undefined) {\n\t\t\tthis.#deliver(listener, this.#value, serviceDeliveryContext(), {\n\t\t\t\tkind: \"hydrate\",\n\t\t\t\tsequence: this.#sequence!,\n\t\t\t});\n\t\t}\n\t\treturn () => this.#listeners.delete(listener);\n\t}\n\n\thydrate(sequence: number, ops: readonly Op[], context: Context): void {\n\t\tlet next: T;\n\t\ttry {\n\t\t\tif (!isBase(ops)) throw new Error(\"Replicated state snapshot is not a base operation batch\");\n\t\t\tnext = this.#store.adopt(applyImmutable<T>(undefined, ops));\n\t\t} catch (error) {\n\t\t\tthis.clear();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#sequence = sequence;\n\t\tthis.#value = next;\n\t\tthis.#deliverAll(context, { kind: \"hydrate\", sequence });\n\t}\n\n\tupdate(sequence: number, ops: readonly Op[], context: Context): void {\n\t\tif (this.#sequence === undefined || this.#value === undefined) {\n\t\t\tthrow new Error(\"Replicated state received an update before hydration\");\n\t\t}\n\t\tif (sequence !== this.#sequence + 1) {\n\t\t\tthis.clear();\n\t\t\tthrow new Error(\"Replicated state update sequence has a gap\");\n\t\t}\n\t\tlet next: T;\n\t\ttry {\n\t\t\tnext = this.#store.adopt(applyImmutable(this.#value, ops));\n\t\t} catch (error) {\n\t\t\tthis.clear();\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#sequence = sequence;\n\t\tthis.#value = next;\n\t\tthis.#deliverAll(context, { kind: \"update\", sequence });\n\t}\n\n\tclear(): void {\n\t\tthis.#value = undefined;\n\t\tthis.#sequence = undefined;\n\t}\n\n\t#deliverAll(context: Context, delivery: ReplicatedStateDelivery): void {\n\t\tif (this.#value === undefined) return;\n\t\tfor (const listener of this.#listeners) this.#deliver(listener, this.#value, context, delivery);\n\t}\n\n\t#deliver(\n\t\tlistener: (value: T, context: Context, delivery: ReplicatedStateDelivery) => void,\n\t\tvalue: T,\n\t\tcontext: Context,\n\t\tdelivery: ReplicatedStateDelivery,\n\t): void {\n\t\ttry {\n\t\t\tlistener(value, context, delivery);\n\t\t} catch (error) {\n\t\t\tthis.#reportError(toError(error));\n\t\t}\n\t}\n}\n\n/** @internal Context for synthetic service deliveries without a caller. */\nexport function serviceDeliveryContext(): Context {\n\t// TODO: Add delivery-scoped cancellation or metadata if deliveries gain an owned lifecycle.\n\treturn BACKGROUND_CONTEXT;\n}\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n"]}
|