@opetope/runtime 0.1.0 → 0.2.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/CHANGELOG.md +27 -0
- package/README.md +24 -13
- package/README.ru.md +26 -13
- package/dist/feature-authoring-types.d.ts +6 -10
- package/dist/feature-authoring.js +1 -1
- package/dist/feature-authoring.js.map +1 -1
- package/dist/feature-body.d.ts +1 -1
- package/dist/feature-body.js.map +1 -1
- package/dist/feature-contribution.d.ts +5 -3
- package/dist/feature-contribution.js +1 -1
- package/dist/feature-contribution.js.map +1 -1
- package/dist/feature-definition-api.d.ts +1 -1
- package/dist/feature-generation.js +1 -1
- package/dist/feature-generation.js.map +1 -1
- package/dist/feature-lazy-generation.d.ts +1 -1
- package/dist/feature-lazy-generation.js +1 -1
- package/dist/feature-lazy-generation.js.map +1 -1
- package/dist/feature-own-values.d.ts +10 -0
- package/dist/feature-own-values.js +2 -0
- package/dist/feature-own-values.js.map +1 -0
- package/dist/feature-port-binding.d.ts +2 -1
- package/dist/feature-port-binding.js +1 -1
- package/dist/feature-port-binding.js.map +1 -1
- package/dist/feature-port.d.ts +13 -23
- package/dist/feature-port.js +1 -1
- package/dist/feature-port.js.map +1 -1
- package/dist/public-module-types.d.ts +1 -1
- package/dist/public-module.d.ts +1 -1
- package/docs/agent-guide.md +11 -4
- package/docs/agent-guide.ru.md +11 -4
- package/docs/cookbook.md +9 -7
- package/docs/cookbook.ru.md +9 -7
- package/docs/decisions.md +15 -1
- package/docs/how-it-works.md +20 -5
- package/docs/how-it-works.ru.md +19 -5
- package/docs/releases.md +38 -17
- package/docs/releases.ru.md +37 -17
- package/docs/spec.md +31 -17
- package/docs/spec.ru.md +32 -18
- package/package.json +3 -3
package/dist/feature-port.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-port.js","sources":["../src/feature-port.ts"],"sourcesContent":["import type { Call, ModelOf, Port, PortRef } from '@opetope/core';\nimport type { ModelIdentity, PortIdentity, PortInput, PortOutput } from '@opetope/core/internal';\nimport { bindPortRef, isPort, isPortRefFor } from '@opetope/core/internal';\n\nimport { isAttachmentCallRef } from './attachment-call-declaration';\nimport type {\n FeatureContribution,\n FeaturePipeBuilder,\n FeatureRegisterBuilder,\n FeatureSlotBuilder,\n} from './feature-contribution';\nimport { isFeatureDataRef } from './feature-model';\nimport type { FeatureModelRef } from './feature-model';\nimport { assertExactDataKeys, assertPlainRecord, dataEntries } from './feature-record';\nimport type { ModuleCallRef, ModuleCallRefIdentity } from './public-module';\n\ndeclare const optionalPortBrand: unique symbol;\ndeclare const portProviderBrand: unique symbol;\n\ntype AnyPort = PortIdentity;\n\n/**\n * `optional(port)` is the weak edge of a port: the application may have no provider, and a call without one\n * settles with `CallError` code `unavailable` instead of failing the consumer (D105).\n */\ninterface OptionalPort<PortType extends AnyPort> {\n readonly [optionalPortBrand]: readonly [PortType];\n}\n\ntype PortRequirementRecord = Readonly<Record<string, AnyPort | OptionalPort<AnyPort>>>;\ntype RequiredPortValues<Requires extends PortRequirementRecord> = {\n readonly [Key in keyof Requires]: Requires[Key] extends OptionalPort<infer PortType>\n ? PortRef<PortInput<PortType>, PortOutput<PortType>> | undefined\n : Requires[Key] extends AnyPort\n ? PortRef<PortInput<Requires[Key]>, PortOutput<Requires[Key]>>\n : never;\n};\n/** Every requirement is a `Call` of this feature: the application chooses the provider, the author calls the ref. */\ntype FeatureRequiredCalls<Id extends string, Requires extends PortRequirementRecord> = {\n readonly [Key in keyof Requires]: Requires[Key] extends OptionalPort<infer PortType>\n ? ModuleCallRef<PortInput<PortType>, PortOutput<PortType>, Id>\n : Requires[Key] extends AnyPort\n ? ModuleCallRef<PortInput<Requires[Key]>, PortOutput<Requires[Key]>, Id>\n : never;\n};\n\ninterface PortProviderBinding<PortType extends AnyPort, Id extends string> {\n readonly [portProviderBrand]: readonly [PortType, Id];\n}\n\ninterface FeaturePortBuilder<Id extends string> {\n <PortType extends AnyPort>(\n port: PortType,\n target: ModuleCallRef<PortInput<NoInfer<PortType>>, PortOutput<NoInfer<PortType>>, Id>,\n ): PortProviderBinding<PortType, Id>;\n <PortType extends AnyPort, Declaration extends ModelIdentity>(\n port: PortType,\n projection: {\n readonly from: FeatureModelRef<Declaration, Id>;\n readonly select: (\n model: ModelOf<NoInfer<Declaration>>,\n ) => Call<PortInput<NoInfer<PortType>>, PortOutput<NoInfer<PortType>>>;\n },\n ): PortProviderBinding<PortType, Id>;\n}\n\ntype PortProviderTarget =\n | { readonly kind: 'call'; readonly ref: ModuleCallRefIdentity }\n | { readonly kind: 'model'; readonly ref: object; readonly select: (model: unknown) => unknown };\n\ntype PortProviderDescriptor = {\n readonly key: string;\n readonly port: AnyPort;\n readonly target: PortProviderTarget;\n};\n/** One section for every extension point: `port` for ports, `slot`, `pipe` and `register` for contributions. */\ntype FeatureProvidesFactory<\n Id extends string,\n Context,\n Evaluation,\n Runtime extends Readonly<Record<string, object>>,\n Provided extends FeatureProvidedRecord<Id>,\n> = (context: {\n readonly own: Runtime;\n readonly pipe: FeaturePipeBuilder<Evaluation>;\n readonly port: FeaturePortBuilder<Id>;\n readonly register: FeatureRegisterBuilder<Context, Evaluation>;\n readonly slot: FeatureSlotBuilder<Context, Evaluation>;\n}) => Provided;\n\ntype FeatureProvidedRecord<Id extends string> = Readonly<\n Record<string, FeatureContribution | PortProviderBinding<AnyPort, Id>>\n>;\n\nconst requiredPorts = new WeakMap<object, AnyPort>();\nconst optionalPorts = new WeakSet<object>();\nconst portProviders = new WeakMap<object, Readonly<{ port: AnyPort; target: PortProviderTarget }>>();\n\n/** An explicit wrapper marks the weak requirement; an authentic bare Port is the strong requirement. */\nfunction optionalPort<PortType extends AnyPort>(port: PortType): OptionalPort<PortType> {\n if (!isPort(port)) throw new TypeError('optional expects an authentic Port.');\n\n const requirement = Object.freeze({}) as OptionalPort<PortType>;\n requiredPorts.set(requirement, port);\n optionalPorts.add(requirement);\n\n return requirement;\n}\n\n/** Tolerant on purpose: the caller may hold any requirement declaration or none at all. */\nfunction isOptionalPortRequirement(requirement: unknown): boolean {\n return typeof requirement === 'object' && requirement !== null && optionalPorts.has(requirement);\n}\n\nfunction getRequiredPort(requirement: unknown): AnyPort {\n if (isPort(requirement)) return requirement;\n\n if (typeof requirement !== 'object' || requirement === null) {\n throw new TypeError('Feature requirement is not an authentic required Port.');\n }\n\n const port = requiredPorts.get(requirement);\n\n if (port === undefined) throw new TypeError('Feature requirement is not an authentic required Port.');\n\n return port;\n}\n\nfunction createPortBuilder<Id extends string>(): FeaturePortBuilder<Id> {\n return <PortType extends AnyPort>(port: PortType, value: unknown) => {\n if (!isPort(port)) throw new TypeError('provides.port takes a port created by definePort.');\n\n let target: PortProviderTarget;\n\n if (isAttachmentCallRef(value)) {\n target = Object.freeze({ kind: 'call', ref: value as unknown as ModuleCallRefIdentity });\n } else {\n assertPlainRecord(value, 'Feature port model projection');\n assertExactDataKeys(\n value,\n dataEntries(value, 'Feature port model projection'),\n ['from', 'select'],\n 'Feature port model projection',\n );\n\n if (!isFeatureDataRef(value['from']) || typeof value['select'] !== 'function') {\n throw new TypeError('provides.port takes an owned Call ref or a model projection with from and select.');\n }\n\n target = Object.freeze({\n kind: 'model',\n ref: value['from'] as object,\n select: value['select'] as (model: unknown) => unknown,\n });\n }\n\n const binding = Object.freeze({}) as PortProviderBinding<PortType, Id>;\n portProviders.set(binding, Object.freeze({ port, target }));\n\n return binding;\n };\n}\n\nfunction snapshotPortProviders(\n runtimeValues: ReadonlySet<object>,\n entries: readonly (readonly [string, unknown])[],\n): readonly PortProviderDescriptor[] {\n const descriptors = entries.map(([key, binding]) => {\n if (typeof binding !== 'object' || binding === null) {\n throw new TypeError(`Feature provider ${key} is not an authentic Port provider.`);\n }\n\n const definition = portProviders.get(binding);\n\n if (definition === undefined) throw new TypeError(`Feature provider ${key} is not an authentic Port provider.`);\n\n if (!runtimeValues.has(definition.target.ref)) {\n throw new TypeError(`Feature provider ${key} must select a call or model ref from its public own.`);\n }\n\n return Object.freeze({ key, port: definition.port, target: definition.target });\n });\n\n return Object.freeze(descriptors);\n}\n\nfunction bindProvidedPort(descriptor: PortProviderDescriptor, target: Call<never, unknown>): PortRef<never, unknown> {\n return bindPortRef(descriptor.port as Port<never, unknown>, target);\n}\n\nfunction assertRequiredPortValue(\n port: AnyPort,\n value: unknown,\n key: string,\n isOptional: boolean,\n): asserts value is PortRef<never, unknown> | undefined {\n if (isOptional && value === undefined) return;\n\n if (!isPortRefFor(port as Port<never, unknown>, value)) {\n throw new TypeError(`openFeature requirement ${key} must be a PortRef for ${port.id}.`);\n }\n}\n\nexport {\n assertRequiredPortValue,\n bindProvidedPort,\n createPortBuilder,\n getRequiredPort,\n isOptionalPortRequirement,\n optionalPort,\n snapshotPortProviders,\n};\nexport type {\n AnyPort,\n FeatureProvidedRecord,\n FeatureProvidesFactory,\n FeatureRequiredCalls,\n OptionalPort,\n PortProviderDescriptor,\n PortRequirementRecord,\n RequiredPortValues,\n};\n"],"names":["requiredPorts","optionalPorts","portProviders","optionalPort","port","isPort","requirement","isOptionalPortRequirement","getRequiredPort","createPortBuilder","value","target","isAttachmentCallRef","assertPlainRecord","assertExactDataKeys","dataEntries","isFeatureDataRef","binding","snapshotPortProviders","runtimeValues","entries","descriptors","key","definition","bindProvidedPort","descriptor","bindPortRef","assertRequiredPortValue","isOptional","isPortRefFor"],"mappings":"kTA8FA,MAAMA,EAAgB,IAAI,QACpBC,EAAgB,IAAI,QACpBC,EAAgB,IAAI,QAG1B,SAASC,EAAuCC,EAAc,CAC5D,GAAI,CAACC,EAAOD,CAAI,EAAG,MAAM,IAAI,UAAU,qCAAqC,EAE5E,MAAME,EAAc,OAAO,OAAO,EAAE,EACpC,OAAAN,EAAc,IAAIM,EAAaF,CAAI,EACnCH,EAAc,IAAIK,CAAW,EAEtBA,CACT,CAGA,SAASC,EAA0BD,EAAoB,CACrD,OAAO,OAAOA,GAAgB,UAAYA,IAAgB,MAAQL,EAAc,IAAIK,CAAW,CACjG,CAEA,SAASE,EAAgBF,EAAoB,CAC3C,GAAID,EAAOC,CAAW,EAAG,OAAOA,EAEhC,GAAI,OAAOA,GAAgB,UAAYA,IAAgB,KACrD,MAAM,IAAI,UAAU,wDAAwD,EAG9E,MAAMF,EAAOJ,EAAc,IAAIM,CAAW,EAE1C,GAAIF,IAAS,OAAW,MAAM,IAAI,UAAU,wDAAwD,EAEpG,OAAOA,CACT,CAEA,SAASK,GAAiB,CACxB,MAAO,CAA2BL,EAAgBM,IAAkB,CAClE,GAAI,CAACL,EAAOD,CAAI,EAAG,MAAM,IAAI,UAAU,mDAAmD,EAE1F,IAAIO,EAEJ,GAAIC,EAAoBF,CAAK,EAC3BC,EAAS,OAAO,OAAO,CAAE,KAAM,OAAQ,IAAKD,EAA2C,MAClF,CASL,GARAG,EAAkBH,EAAO,+BAA+B,EACxDI,EACEJ,EACAK,EAAYL,EAAO,+BAA+B,EAClD,CAAC,OAAQ,QAAQ,EACjB,+BAA+B,EAG7B,CAACM,EAAiBN,EAAM,IAAO,GAAK,OAAOA,EAAM,QAAc,WACjE,MAAM,IAAI,UAAU,mFAAmF,EAGzGC,EAAS,OAAO,OAAO,CACrB,KAAM,QACN,IAAKD,EAAM,KACX,OAAQA,EAAM,MACf,CAAA,CACH,CAEA,MAAMO,EAAU,OAAO,OAAO,EAAE,EAChC,OAAAf,EAAc,IAAIe,EAAS,OAAO,OAAO,CAAE,KAAAb,EAAM,OAAAO,CAAM,CAAE,CAAC,EAEnDM,CACT,CACF,CAEA,SAASC,EACPC,EACAC,EAAgD,CAEhD,MAAMC,EAAcD,EAAQ,IAAI,CAAC,CAACE,EAAKL,CAAO,IAAK,CACjD,GAAI,OAAOA,GAAY,UAAYA,IAAY,KAC7C,MAAM,IAAI,UAAU,oBAAoBK,CAAG,qCAAqC,EAGlF,MAAMC,EAAarB,EAAc,IAAIe,CAAO,EAE5C,GAAIM,IAAe,OAAW,MAAM,IAAI,UAAU,oBAAoBD,CAAG,qCAAqC,EAE9G,GAAI,CAACH,EAAc,IAAII,EAAW,OAAO,GAAG,EAC1C,MAAM,IAAI,UAAU,oBAAoBD,CAAG,uDAAuD,EAGpG,OAAO,OAAO,OAAO,CAAE,IAAAA,EAAK,KAAMC,EAAW,KAAM,OAAQA,EAAW,OAAQ,CAChF,CAAC,EAED,OAAO,OAAO,OAAOF,CAAW,CAClC,CAEA,SAASG,EAAiBC,EAAoCd,EAA4B,CACxF,OAAOe,EAAYD,EAAW,KAA8Bd,CAAM,CACpE,CAEA,SAASgB,EACPvB,EACAM,EACAY,EACAM,EAAmB,CAEnB,GAAI,EAAAA,GAAclB,IAAU,SAExB,CAACmB,EAAazB,EAA8BM,CAAK,EACnD,MAAM,IAAI,UAAU,2BAA2BY,CAAG,0BAA0BlB,EAAK,EAAE,GAAG,CAE1F"}
|
|
1
|
+
{"version":3,"file":"feature-port.js","sources":["../src/feature-port.ts"],"sourcesContent":["import type { Call, Port, PortRef } from '@opetope/core';\nimport type { PortIdentity, PortInput, PortOutput } from '@opetope/core/internal';\nimport { bindPortRef, isPort, isPortRefFor } from '@opetope/core/internal';\n\nimport type { FeatureOwnValues } from './feature-authoring-types';\nimport type {\n FeatureContribution,\n FeaturePipeBuilder,\n FeatureRegisterBuilder,\n FeatureSlotBuilder,\n} from './feature-contribution';\nimport type { ModuleCallRef } from './public-module';\n\ndeclare const optionalPortBrand: unique symbol;\ndeclare const portProviderBrand: unique symbol;\n\ntype AnyPort = PortIdentity;\n\n/**\n * `optional(port)` is the weak edge of a port: the application may have no provider, and a call without one\n * settles with `CallError` code `unavailable` instead of failing the consumer (D105).\n */\ninterface OptionalPort<PortType extends AnyPort> {\n readonly [optionalPortBrand]: readonly [PortType];\n}\n\ntype PortRequirementRecord = Readonly<Record<string, AnyPort | OptionalPort<AnyPort>>>;\ntype RequiredPortValues<Requires extends PortRequirementRecord> = {\n readonly [Key in keyof Requires]: Requires[Key] extends OptionalPort<infer PortType>\n ? PortRef<PortInput<PortType>, PortOutput<PortType>> | undefined\n : Requires[Key] extends AnyPort\n ? PortRef<PortInput<Requires[Key]>, PortOutput<Requires[Key]>>\n : never;\n};\n/** Every requirement is a `Call` of this feature: the application chooses the provider, the author calls the ref. */\ntype FeatureRequiredCalls<Id extends string, Requires extends PortRequirementRecord> = {\n readonly [Key in keyof Requires]: Requires[Key] extends OptionalPort<infer PortType>\n ? ModuleCallRef<PortInput<PortType>, PortOutput<PortType>, Id>\n : Requires[Key] extends AnyPort\n ? ModuleCallRef<PortInput<Requires[Key]>, PortOutput<Requires[Key]>, Id>\n : never;\n};\n\ninterface PortProviderBinding<PortType extends AnyPort, Id extends string> {\n readonly [portProviderBrand]: readonly [PortType, Id];\n}\n\ntype FeaturePortBuilder<Id extends string, Own> = <PortType extends AnyPort>(\n port: PortType,\n select: (context: { readonly own: Own }) => Call<PortInput<NoInfer<PortType>>, PortOutput<NoInfer<PortType>>>,\n) => PortProviderBinding<PortType, Id>;\n\ntype PortProviderTarget = (context: { readonly own: Readonly<Record<string, unknown>> }) => unknown;\n\ntype PortProviderDescriptor = {\n readonly key: string;\n readonly port: AnyPort;\n readonly target: PortProviderTarget;\n};\n/** One section for every extension point: `port` for ports, `slot`, `pipe` and `register` for contributions. */\ntype FeatureProvidesFactory<\n Id extends string,\n Context,\n Evaluation,\n Runtime extends Readonly<Record<string, object>>,\n Provided extends FeatureProvidedRecord<Id>,\n> = (context: {\n readonly pipe: FeaturePipeBuilder<Evaluation>;\n readonly port: FeaturePortBuilder<Id, FeatureOwnValues<Id, Runtime>>;\n readonly register: FeatureRegisterBuilder<Context, Evaluation>;\n readonly slot: FeatureSlotBuilder<Context, Evaluation>;\n}) => Provided;\n\ntype FeatureProvidedRecord<Id extends string> = Readonly<\n Record<string, FeatureContribution | PortProviderBinding<AnyPort, Id>>\n>;\n\nconst requiredPorts = new WeakMap<object, AnyPort>();\nconst optionalPorts = new WeakSet<object>();\nconst portProviders = new WeakMap<object, Readonly<{ port: AnyPort; target: PortProviderTarget }>>();\n\n/** An explicit wrapper marks the weak requirement; an authentic bare Port is the strong requirement. */\nfunction optionalPort<PortType extends AnyPort>(port: PortType): OptionalPort<PortType> {\n if (!isPort(port)) throw new TypeError('optional expects an authentic Port.');\n\n const requirement = Object.freeze({}) as OptionalPort<PortType>;\n requiredPorts.set(requirement, port);\n optionalPorts.add(requirement);\n\n return requirement;\n}\n\n/** Tolerant on purpose: the caller may hold any requirement declaration or none at all. */\nfunction isOptionalPortRequirement(requirement: unknown): boolean {\n return typeof requirement === 'object' && requirement !== null && optionalPorts.has(requirement);\n}\n\nfunction getRequiredPort(requirement: unknown): AnyPort {\n if (isPort(requirement)) return requirement;\n\n if (typeof requirement !== 'object' || requirement === null) {\n throw new TypeError('Feature requirement is not an authentic required Port.');\n }\n\n const port = requiredPorts.get(requirement);\n\n if (port === undefined) throw new TypeError('Feature requirement is not an authentic required Port.');\n\n return port;\n}\n\nfunction createPortBuilder<Id extends string, Own>(): FeaturePortBuilder<Id, Own> {\n return <PortType extends AnyPort>(port: PortType, value: unknown) => {\n if (!isPort(port)) throw new TypeError('provides.port takes a port created by definePort.');\n\n if (typeof value !== 'function') throw new TypeError('provides.port takes a selector of an owned Call.');\n\n const target = value as PortProviderTarget;\n\n const binding = Object.freeze({}) as PortProviderBinding<PortType, Id>;\n portProviders.set(binding, Object.freeze({ port, target }));\n\n return binding;\n };\n}\n\nfunction snapshotPortProviders(entries: readonly (readonly [string, unknown])[]): readonly PortProviderDescriptor[] {\n const descriptors = entries.map(([key, binding]) => {\n if (typeof binding !== 'object' || binding === null) {\n throw new TypeError(`Feature provider ${key} is not an authentic Port provider.`);\n }\n\n const definition = portProviders.get(binding);\n\n if (definition === undefined) throw new TypeError(`Feature provider ${key} is not an authentic Port provider.`);\n\n return Object.freeze({ key, port: definition.port, target: definition.target });\n });\n\n return Object.freeze(descriptors);\n}\n\nfunction bindProvidedPort(descriptor: PortProviderDescriptor, target: Call<never, unknown>): PortRef<never, unknown> {\n return bindPortRef(descriptor.port as Port<never, unknown>, target);\n}\n\nfunction assertRequiredPortValue(\n port: AnyPort,\n value: unknown,\n key: string,\n isOptional: boolean,\n): asserts value is PortRef<never, unknown> | undefined {\n if (isOptional && value === undefined) return;\n\n if (!isPortRefFor(port as Port<never, unknown>, value)) {\n throw new TypeError(`openFeature requirement ${key} must be a PortRef for ${port.id}.`);\n }\n}\n\nexport {\n assertRequiredPortValue,\n bindProvidedPort,\n createPortBuilder,\n getRequiredPort,\n isOptionalPortRequirement,\n optionalPort,\n snapshotPortProviders,\n};\nexport type {\n AnyPort,\n FeatureProvidedRecord,\n FeatureProvidesFactory,\n FeatureRequiredCalls,\n OptionalPort,\n PortProviderDescriptor,\n PortRequirementRecord,\n RequiredPortValues,\n};\n"],"names":["requiredPorts","optionalPorts","portProviders","optionalPort","port","isPort","requirement","isOptionalPortRequirement","getRequiredPort","createPortBuilder","value","target","binding","snapshotPortProviders","entries","descriptors","key","definition","bindProvidedPort","descriptor","bindPortRef","assertRequiredPortValue","isOptional","isPortRefFor"],"mappings":"mFA6EA,MAAMA,EAAgB,IAAI,QACpBC,EAAgB,IAAI,QACpBC,EAAgB,IAAI,QAG1B,SAASC,EAAuCC,EAAc,CAC5D,GAAI,CAACC,EAAOD,CAAI,EAAG,MAAM,IAAI,UAAU,qCAAqC,EAE5E,MAAME,EAAc,OAAO,OAAO,EAAE,EACpC,OAAAN,EAAc,IAAIM,EAAaF,CAAI,EACnCH,EAAc,IAAIK,CAAW,EAEtBA,CACT,CAGA,SAASC,EAA0BD,EAAoB,CACrD,OAAO,OAAOA,GAAgB,UAAYA,IAAgB,MAAQL,EAAc,IAAIK,CAAW,CACjG,CAEA,SAASE,EAAgBF,EAAoB,CAC3C,GAAID,EAAOC,CAAW,EAAG,OAAOA,EAEhC,GAAI,OAAOA,GAAgB,UAAYA,IAAgB,KACrD,MAAM,IAAI,UAAU,wDAAwD,EAG9E,MAAMF,EAAOJ,EAAc,IAAIM,CAAW,EAE1C,GAAIF,IAAS,OAAW,MAAM,IAAI,UAAU,wDAAwD,EAEpG,OAAOA,CACT,CAEA,SAASK,GAAiB,CACxB,MAAO,CAA2BL,EAAgBM,IAAkB,CAClE,GAAI,CAACL,EAAOD,CAAI,EAAG,MAAM,IAAI,UAAU,mDAAmD,EAE1F,GAAI,OAAOM,GAAU,WAAY,MAAM,IAAI,UAAU,kDAAkD,EAEvG,MAAMC,EAASD,EAETE,EAAU,OAAO,OAAO,EAAE,EAChC,OAAAV,EAAc,IAAIU,EAAS,OAAO,OAAO,CAAE,KAAAR,EAAM,OAAAO,CAAM,CAAE,CAAC,EAEnDC,CACT,CACF,CAEA,SAASC,EAAsBC,EAAgD,CAC7E,MAAMC,EAAcD,EAAQ,IAAI,CAAC,CAACE,EAAKJ,CAAO,IAAK,CACjD,GAAI,OAAOA,GAAY,UAAYA,IAAY,KAC7C,MAAM,IAAI,UAAU,oBAAoBI,CAAG,qCAAqC,EAGlF,MAAMC,EAAaf,EAAc,IAAIU,CAAO,EAE5C,GAAIK,IAAe,OAAW,MAAM,IAAI,UAAU,oBAAoBD,CAAG,qCAAqC,EAE9G,OAAO,OAAO,OAAO,CAAE,IAAAA,EAAK,KAAMC,EAAW,KAAM,OAAQA,EAAW,OAAQ,CAChF,CAAC,EAED,OAAO,OAAO,OAAOF,CAAW,CAClC,CAEA,SAASG,EAAiBC,EAAoCR,EAA4B,CACxF,OAAOS,EAAYD,EAAW,KAA8BR,CAAM,CACpE,CAEA,SAASU,EACPjB,EACAM,EACAM,EACAM,EAAmB,CAEnB,GAAI,EAAAA,GAAcZ,IAAU,SAExB,CAACa,EAAanB,EAA8BM,CAAK,EACnD,MAAM,IAAI,UAAU,2BAA2BM,CAAG,0BAA0BZ,EAAK,EAAE,GAAG,CAE1F"}
|
|
@@ -292,4 +292,4 @@ type ModuleScopeHandleState<Value> = {
|
|
|
292
292
|
record: ModuleInstanceRecord | undefined;
|
|
293
293
|
readonly runtime: ModuleScopeInstance<Value>;
|
|
294
294
|
};
|
|
295
|
-
export type { Awaitable, CleanupFailurePolicy, ErrorReporter, ExactInput, ExactNonUnionRuntime, InternalModuleRuntimeRecord, InternalModuleRuntimeRef, ModuleAttachmentCleanupFailureDiagnostic, ModuleAttachmentCloseContext, ModuleAttachmentOpenContext, ModuleAttachmentOptions, ModuleAttachmentRef, ModuleAttachmentRefIdentity, ModuleAttachmentRetirementFailure, ModuleCallBuilder, ModuleCallContext, ModuleCallOptions, ModuleCallRef,
|
|
295
|
+
export type { Awaitable, CleanupFailurePolicy, ErrorReporter, ExactInput, ExactNonUnionRuntime, InternalModuleRuntimeRecord, InternalModuleRuntimeRef, ModuleAttachmentCleanupFailureDiagnostic, ModuleAttachmentCloseContext, ModuleAttachmentOpenContext, ModuleAttachmentOptions, ModuleAttachmentRef, ModuleAttachmentRefIdentity, ModuleAttachmentRetirementFailure, ModuleCallBuilder, ModuleCallContext, ModuleCallOptions, ModuleCallRef, ModuleCleanupErrorDiagnostic, ModuleCleanupFailureDiagnostic, ModuleDefinition, ModuleDefinitionOptions, ModuleDefinitionRecord, ModuleDrainOutcome, ModuleInstance, ModuleInstanceOptions, ModuleInstanceRecord, ModuleInstanceRef, ModuleParticipantRetirementFailure, ModuleRetirement, ModuleRetirementFailure, ModuleRetirementHandleState, ModuleRetirementParticipant, ModuleRetirementParticipantHandle, ModuleRetirementQuarantinedOutcome, ModuleRetirementRecord, ModuleRuntimeBuilder, ModuleRuntimeRecord, ModuleRuntimeRef, ModuleRuntimeRefIdentity, ModuleScopeBuilder, ModuleScopeDefinitionEntry, ModuleScopeDrainOutcome, ModuleScopeHandleState, ModuleScopeInstance, ModuleScopeNotReadyOutcome, ModuleScopeOpenOutcome, ModuleScopeRef, ModuleScopeRefIdentity, ModuleScopeRetirement, ModuleScopeRetirementFailure, ParticipantFenceFrontier, ParticipantHandleFrontier, RegisteredParticipant, RetirementFrontier, ScopeFenceFrontier, ScopeFrontier, };
|
package/dist/public-module.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { defineModule, getModuleScopeEntries, getModuleTemplateIR } from './public-module-definition.js';
|
|
2
2
|
export { instantiateModule, registerModuleRetirementParticipant, resolveModuleCallTarget, } from './public-module-instance.js';
|
|
3
3
|
export { projectModuleCleanupFailures } from './public-module-retirement-diagnostics.js';
|
|
4
|
-
export type { CleanupFailurePolicy, ErrorReporter, ExactNonUnionRuntime, ModuleAttachmentCleanupFailureDiagnostic, ModuleAttachmentCloseContext, ModuleAttachmentOpenContext, ModuleAttachmentOptions, ModuleAttachmentRef, ModuleAttachmentRefIdentity, ModuleCallContext, ModuleCallRef,
|
|
4
|
+
export type { CleanupFailurePolicy, ErrorReporter, ExactNonUnionRuntime, ModuleAttachmentCleanupFailureDiagnostic, ModuleAttachmentCloseContext, ModuleAttachmentOpenContext, ModuleAttachmentOptions, ModuleAttachmentRef, ModuleAttachmentRefIdentity, ModuleCallContext, ModuleCallRef, ModuleCleanupErrorDiagnostic, ModuleCleanupFailureDiagnostic, ModuleDefinition, ModuleDefinitionOptions, ModuleDrainOutcome, ModuleInstance, ModuleInstanceOptions, ModuleInstanceRef, ModuleParticipantRetirementFailure, ModuleRetirement, ModuleRetirementFailure, ModuleRetirementParticipant, ModuleRetirementParticipantHandle, ModuleRetirementQuarantinedOutcome, ModuleRuntimeBuilder, ModuleRuntimeRecord, ModuleRuntimeRef, ModuleRuntimeRefIdentity, ModuleScopeDrainOutcome, ModuleScopeInstance, ModuleScopeOpenOutcome, ModuleScopeRef, ModuleScopeRetirement, ModuleScopeRetirementFailure, } from './public-module-types.js';
|
package/docs/agent-guide.md
CHANGED
|
@@ -44,10 +44,17 @@ A feature whose implementation is heavy is written as a header and a body: the h
|
|
|
44
44
|
the body loads when an instance opens (D186, D207). `defineFeature.preload(feature)` fetches that code early without
|
|
45
45
|
opening anything; on a feature with no body it is a successful no-op (D208).
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
factories and model factories) receive materialized values.
|
|
47
|
+
The `own` builder sees refs for imports and requirements. The outer `provides` receives only `{ port, register, slot, pipe }`.
|
|
48
|
+
Instance stages (`exports`, port selectors, contribution factories and model factories) receive materialized values.
|
|
49
|
+
Use a nested factory's `own` to register a private
|
|
50
|
+
model call: `register(target, ({ own }) => ({ key: 'submit', value: own.order.submit }))`; no export is required.
|
|
51
|
+
`register` receives `{ own, imports }`; `slot` receives `{ own, imports, model }`, with `model` declaring a
|
|
52
|
+
per-mount UI model. Their `own` belongs to that exact instance. Keep `exports` for the API other features import;
|
|
53
|
+
contribution contexts have no `exports` or `instance` field (D255).
|
|
54
|
+
Compose model dependencies with a readonly map of
|
|
49
55
|
current-feature imports and call refs, including `requires.x`; optional imports keep their lookup projection.
|
|
50
|
-
Use `port(Port, {
|
|
56
|
+
Use `port(Port, ({ own }) => own.model.call)` or `port(Port, ({ own }) => own.call)`; the selector receives only
|
|
57
|
+
`{ own }` and runs once during instance preparation, before readiness and publication (D255). `calls(imports.x, keys)`
|
|
51
58
|
accepts host methods, exported calls of a hard feature import, and exported calls over `optional(feature)`, where
|
|
52
59
|
they answer `CallError` `unavailable` while nothing provides them. Read the data of a weak edge with
|
|
53
60
|
`fromOptional(source, select, { missing })` instead of unfolding `lookup.kind` by hand (D187). For resources and streams, `target` selects a
|
|
@@ -61,7 +68,7 @@ destructuring and custom context types together; factory `call`, `calls` and the
|
|
|
61
68
|
The nested invocation inherits cancellation, authority and the lane stack; it does not choose a new policy.
|
|
62
69
|
|
|
63
70
|
A contribution's `when` is either a `Readable<boolean>` or a pure, synchronous predicate of the instance —
|
|
64
|
-
`({
|
|
71
|
+
`({ own, imports, read }) => boolean`. Its `read` only records what the answer depends on: do not write,
|
|
65
72
|
call or await inside it, and expect it to run again whenever a source it read changes. In that context `own` is
|
|
66
73
|
materialized, so a model field is a `Readable` and not the declaration ref (D220). A `pipe` declares its handler as
|
|
67
74
|
a descriptor with one key — `pipe(target, { fold: (value, meta, context) => next })` — whose `context` is that same
|
package/docs/agent-guide.ru.md
CHANGED
|
@@ -41,10 +41,17 @@ policy?, within })`; данные по ключу → `resource`; поток →
|
|
|
41
41
|
загружается при открытии экземпляра (D186, D207). `defineFeature.preload(feature)` подтягивает этот код заранее,
|
|
42
42
|
ничего не открывая; для фичи без тела это успешный no-op (D208).
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
вкладов и
|
|
44
|
+
Builder `own` видит refs импортов и требований. Внешняя `provides` получает только `{ port, register, slot, pipe }`.
|
|
45
|
+
Стадии экземпляра (`exports`, селекторы портов, фабрики вкладов и моделей) получают материализованные значения.
|
|
46
|
+
Для регистрации вызова внутренней модели
|
|
47
|
+
берите `own` вложенной фабрики: `register(target, ({ own }) => ({ key: 'submit', value: own.order.submit }))`;
|
|
48
|
+
экспорт не нужен. `register` получает `{ own, imports }`, а `slot` — `{ own, imports, model }`, где `model`
|
|
49
|
+
объявляет UI-модель на каждое монтирование. Их `own` принадлежит конкретному экземпляру. Оставляйте `exports`
|
|
50
|
+
для API, который импортируют другие фичи; в контекстах вкладов нет полей `exports` и `instance` (D255).
|
|
51
|
+
Зависимости модели собираются readonly-записью
|
|
46
52
|
импортов и call refs текущей фичи, включая `requires.x`; optional-импорты сохраняют lookup-проекцию.
|
|
47
|
-
|
|
53
|
+
Используйте `port(Port, ({ own }) => own.model.call)` или `port(Port, ({ own }) => own.call)`; селектор получает
|
|
54
|
+
только `{ own }` и выполняется один раз при подготовке экземпляра, до readiness и публикации (D255).
|
|
48
55
|
`calls(imports.x, keys)` принимает методы хоста, экспортированные вызовы жёсткого импорта фичи и экспортированные
|
|
49
56
|
вызовы поверх `optional(feature)` — там они отвечают `CallError` `unavailable`, пока провайдера нет. Данные слабого
|
|
50
57
|
ребра читайте через `fromOptional(source, select, { missing })`, а не разворачивая `lookup.kind` руками (D187). У ресурсов
|
|
@@ -58,7 +65,7 @@ policy?, within })`; данные по ключу → `resource`; поток →
|
|
|
58
65
|
Вложенный вызов наследует отмену, авторитет и стек lane; новую политику он не выбирает.
|
|
59
66
|
|
|
60
67
|
`when` у вклада это либо `Readable<boolean>`, либо чистый синхронный предикат экземпляра —
|
|
61
|
-
`({
|
|
68
|
+
`({ own, imports, read }) => boolean`. Его `read` только записывает, от чего зависит ответ: внутри нельзя
|
|
62
69
|
писать, вызывать и ждать, а сам предикат выполнится снова при смене любого прочитанного источника. В этом контексте
|
|
63
70
|
`own` материализован: поле модели это `Readable`, а не ref декларации (D220). `pipe` объявляет обработчик
|
|
64
71
|
дескриптором с одним ключом — `pipe(target, { fold: (value, meta, context) => next })`, — где `context` это тот же
|
package/docs/cookbook.md
CHANGED
|
@@ -20,7 +20,7 @@ const feature = defineFeature({
|
|
|
20
20
|
requires: { resolve: resolveItemPort },
|
|
21
21
|
own: ({ imports, requires, calls, attach, call, lane, effect, event, resource, stream, scope, model }) => ({ … }),
|
|
22
22
|
exports: ({ own }) => ({ x: own.x }), // Call | Readable | Resource out of own; a whole model is not allowed
|
|
23
|
-
provides: ({ slot, pipe, register, port
|
|
23
|
+
provides: ({ slot, pipe, register, port }) => ({ … }),
|
|
24
24
|
});
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -46,7 +46,7 @@ const catalogResolveFeature = defineFeature({
|
|
|
46
46
|
id: catalogResolveFeatureId,
|
|
47
47
|
imports: { platform: catalogResolveSource },
|
|
48
48
|
own: ({ calls, imports }) => ({ ...calls(imports.platform, ['resolveItem']) }), // host methods as Call
|
|
49
|
-
provides: ({
|
|
49
|
+
provides: ({ port }) => ({ catalog: port(resolveItemPort, ({ own }) => own.resolveItem) }),
|
|
50
50
|
});
|
|
51
51
|
```
|
|
52
52
|
|
|
@@ -180,15 +180,16 @@ parameters are supported. A single parameter is always the input: for a signal-o
|
|
|
180
180
|
with `context.call({ run: (_input: void, { signal }) => deps.load(signal) })` or use an explicit adapter.
|
|
181
181
|
|
|
182
182
|
A model can compose a host import and a required call without receiving the feature context. Its factory declares
|
|
183
|
-
its own dependency interface. A model call may also implement a port; the
|
|
184
|
-
and
|
|
183
|
+
its own dependency interface. A model call may also implement a port; the selector receives `{ own }` and runs once
|
|
184
|
+
during instance preparation, before readiness and publication. The provider's lifetime fences the selected call
|
|
185
|
+
even when it is passed through from a dependency (D169, D255).
|
|
185
186
|
|
|
186
187
|
```ts
|
|
187
188
|
own: ({ imports, model, requires }) => ({
|
|
188
189
|
order: model(OrderModel, { platform: imports.platform, lookup: requires.lookup }, createOrderModel),
|
|
189
190
|
}),
|
|
190
|
-
provides: ({
|
|
191
|
-
submit: port(SubmitPort, {
|
|
191
|
+
provides: ({ port }) => ({
|
|
192
|
+
submit: port(SubmitPort, ({ own }) => own.order.submit),
|
|
192
193
|
}),
|
|
193
194
|
```
|
|
194
195
|
|
|
@@ -624,7 +625,7 @@ const paymentMethods = defineRegistry<string, PaymentMethodEntry>({ id: 'checkou
|
|
|
624
625
|
|
|
625
626
|
// feature.ts — the entry is a contribution, so it opens and closes with the instance that made it
|
|
626
627
|
provides: ({ register }) => ({
|
|
627
|
-
card: register(paymentMethods, ({
|
|
628
|
+
card: register(paymentMethods, ({ own }) => ({ key: 'card', value: { submit: own.order.submit, title: 'Card' } })),
|
|
628
629
|
}),
|
|
629
630
|
|
|
630
631
|
// UI reads the target, never the provider's module
|
|
@@ -632,6 +633,7 @@ const method = useReadable(paymentMethods.select(selectedKey));
|
|
|
632
633
|
const methods = useReadable(paymentMethods.list);
|
|
633
634
|
```
|
|
634
635
|
|
|
636
|
+
`own.order` is the ready model of this feature; registering its `submit` Call requires no public export (D255).
|
|
635
637
|
The publication is atomic and owned: the entry lands when the instance is ready, the fence withdraws it on close, a
|
|
636
638
|
duplicate key is refused before anything moves, the order is `priority` and then id, and the inspector lists the
|
|
637
639
|
entry with the feature that owns it (D127, D196). `select(key)` answers the same `Lookup` as a weak edge, so an
|
package/docs/cookbook.ru.md
CHANGED
|
@@ -19,7 +19,7 @@ const feature = defineFeature({
|
|
|
19
19
|
requires: { resolve: resolveItemPort },
|
|
20
20
|
own: ({ imports, requires, calls, attach, call, lane, effect, event, resource, stream, scope, model }) => ({ … }),
|
|
21
21
|
exports: ({ own }) => ({ x: own.x }), // Call | Readable | Resource из own; модель целиком — нельзя
|
|
22
|
-
provides: ({ slot, pipe, register, port
|
|
22
|
+
provides: ({ slot, pipe, register, port }) => ({ … }),
|
|
23
23
|
});
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -44,7 +44,7 @@ const catalogResolveFeature = defineFeature({
|
|
|
44
44
|
id: catalogResolveFeatureId,
|
|
45
45
|
imports: { platform: catalogResolveSource },
|
|
46
46
|
own: ({ calls, imports }) => ({ ...calls(imports.platform, ['resolveItem']) }), // методы хоста как Call
|
|
47
|
-
provides: ({
|
|
47
|
+
provides: ({ port }) => ({ catalog: port(resolveItemPort, ({ own }) => own.resolveItem) }),
|
|
48
48
|
});
|
|
49
49
|
```
|
|
50
50
|
|
|
@@ -179,15 +179,16 @@ exports: ({ own }) => ({ tasks: own.tasks }), // Resource наружу; вла
|
|
|
179
179
|
`context.call({ run: (_input: void, { signal }) => deps.load(signal) })` или используйте явный адаптер.
|
|
180
180
|
|
|
181
181
|
Модель может соединить импорт хоста и требуемый вызов, не получая контекст фичи. Фабрика объявляет свой интерфейс
|
|
182
|
-
зависимостей. Вызов модели также может реализовать порт;
|
|
183
|
-
жизни провайдера ограничивает выбранный вызов, даже
|
|
182
|
+
зависимостей. Вызов модели также может реализовать порт; селектор получает `{ own }` и выполняется один раз при
|
|
183
|
+
подготовке экземпляра, до readiness и публикации. Время жизни провайдера ограничивает выбранный вызов, даже
|
|
184
|
+
переданный напрямую из зависимости (D169, D255).
|
|
184
185
|
|
|
185
186
|
```ts
|
|
186
187
|
own: ({ imports, model, requires }) => ({
|
|
187
188
|
order: model(OrderModel, { platform: imports.platform, lookup: requires.lookup }, createOrderModel),
|
|
188
189
|
}),
|
|
189
|
-
provides: ({
|
|
190
|
-
submit: port(SubmitPort, {
|
|
190
|
+
provides: ({ port }) => ({
|
|
191
|
+
submit: port(SubmitPort, ({ own }) => own.order.submit),
|
|
191
192
|
}),
|
|
192
193
|
```
|
|
193
194
|
|
|
@@ -620,7 +621,7 @@ const paymentMethods = defineRegistry<string, PaymentMethodEntry>({ id: 'checkou
|
|
|
620
621
|
|
|
621
622
|
// feature.ts — запись это вклад, поэтому она открывается и закрывается с экземпляром, который её сделал
|
|
622
623
|
provides: ({ register }) => ({
|
|
623
|
-
card: register(paymentMethods, ({
|
|
624
|
+
card: register(paymentMethods, ({ own }) => ({ key: 'card', value: { submit: own.order.submit, title: 'Card' } })),
|
|
624
625
|
}),
|
|
625
626
|
|
|
626
627
|
// UI читает цель, а не модуль провайдера
|
|
@@ -628,6 +629,7 @@ const method = useReadable(paymentMethods.select(selectedKey));
|
|
|
628
629
|
const methods = useReadable(paymentMethods.list);
|
|
629
630
|
```
|
|
630
631
|
|
|
632
|
+
`own.order` — готовая модель этой фичи; регистрация её Call `submit` не требует публичного экспорта (D255).
|
|
631
633
|
Публикация атомарна и принадлежит владельцу: запись ложится, когда экземпляр готов, fence снимает её на закрытии,
|
|
632
634
|
дубль ключа отвергается до того, как что-то сдвинулось, порядок это `priority`, затем id, а инспектор показывает
|
|
633
635
|
запись вместе с фичей-владельцем (D127, D196). `select(key)` отвечает тем же `Lookup`, что и слабое ребро, поэтому
|
package/docs/decisions.md
CHANGED
|
@@ -1392,7 +1392,7 @@ cap панели поднимается с 20 до 21 kb. Поднятие по
|
|
|
1392
1392
|
|
|
1393
1393
|
## D247 — `event` допускает `backpressure: latest()`, дефолт не меняется
|
|
1394
1394
|
|
|
1395
|
-
|
|
1395
|
+
оба authoring-пути, `own.event` и `ModelContext.event`, допускают необязательную опцию `backpressure` с тем же публичным словом `latest()`, которое требует `stream`: пока `run` исполняется и один payload ждёт, новый payload заменяет ожидающего и запись отказа не пишется. Дефолт без опции остаётся прежним: слот принадлежит payload, занявшему его первым, а новый `emit` отбрасывается разделяемой записью `queue-capacity`.
|
|
1396
1396
|
|
|
1397
1397
|
<a id="d248"></a>
|
|
1398
1398
|
|
|
@@ -1435,3 +1435,17 @@ Schema 11 заменяет смешанный метод [D252](#d252): для
|
|
|
1435
1435
|
## D254 — npm поставляет минифицированный ESM с исходными картами
|
|
1436
1436
|
|
|
1437
1437
|
Пакеты собирают JavaScript с `minify: true` и `keepNames: false`; декларации `.d.ts` сохраняются отдельно, source maps включают исходный TypeScript в `sourcesContent`. Значения `fn.name` и `constructor.name`, полученные через рефлексию, не входят в стабильный API. Явные ID деклараций, публичные `error.name`/`error.code` и проверки `instanceof` с экспортируемыми классами ошибок сохраняют контракт и проверяются на реальных tarball-потребителях. Source-map-aware инструменты позволяют найти исходный TypeScript; карты раскрывают реализацию, а не скрывают её. Политика уменьшает несжатый JavaScript, не обещая уменьшения tarball или итогового бандла хоста: эти размеры измеряются отдельно.
|
|
1438
|
+
|
|
1439
|
+
<a id="d255"></a>
|
|
1440
|
+
|
|
1441
|
+
## D255 — `provides` объявляет цели, вложенные колбэки выбирают готовый `own`
|
|
1442
|
+
|
|
1443
|
+
Внешняя `provides` получает ровно `{ port, register, slot, pipe }`: только builders, без `own` или других значений экземпляра. Цели, ids и приоритеты фиксируются синхронно при объявлении и известны компилятору до открытия. `port(Port, ({ own }) => own.model.call)` и `port(Port, ({ own }) => own.call)` используют единственную форму селектора: он получает только `{ own }` и один раз выбирает подлинный Call текущего экземпляра при подготовке, после создания моделей, до readiness и публикации вкладов. Прямые refs и дескрипторы `{ from, select }` больше не принимаются. Выбранный вызов сохраняет фенс провайдера, включая переданный напрямую вызов зависимости.
|
|
1444
|
+
|
|
1445
|
+
Результат селектора должен быть подлинным Call из прочитанных им значений `own`, включая поля моделей. Подлинный, но чужой Call, обычная функция и другое значение отвергаются. Чтение для этой проверки остаётся ленивым: соседние lifecycle-only поля не раскрываются. Актуальность экземпляра проверяется до и после пользовательского селектора; синхронный retirement внутри него не допускает следующие селекторы, а уже владеемая работа дренируется.
|
|
1446
|
+
|
|
1447
|
+
Вложенная фабрика `register` получает `{ own, imports }`, фабрика `slot` — `{ own, imports, model }`, а предикат `when` и обработчик `pipe.fold` — `{ own, imports, read }`. Это точные поверхности без aliases: `exports` и `instance` удалены из контекстов вкладов, `model` доступен только фабрике `slot`, где объявляет UI-модель на каждое монтирование. `exports` остаётся секцией публичного API фичи для её потребителей; внутренней композиции доступны `own` и объявленные `imports`.
|
|
1448
|
+
|
|
1449
|
+
Все вложенные контексты получают тот же материализованный `own`, что секция `exports`: модель становится записью её полей, call ref — готовым `Call`, resource/stream ref — ресурсом. Регистрация вызова внутренней модели не требует публикации этого вызова в `exports`. Модели создаются до селекторов портов и фабрик вкладов, каждый экземпляр получает свои значения, запись раскрывает поля лениво, а lifetime вызовов и read-only проекция owned state сохраняются. Builder секции `own` по-прежнему использует refs импортов и требований для декларации зависимостей.
|
|
1450
|
+
|
|
1451
|
+
Миграция меняет исходный код: внешний `own` убирается из `provides`, выбор вызова переносится во второй аргумент `port` как `({ own }) => own.call` или `({ own }) => own.model.call`. Вместо `context.exports.submit` выбирается исходный `context.own.order.submit`; только внутренние экспорты удаляются из секции `exports`. Вместо `context.instance` с внутренним bind/resolve выбирается готовое значение из `own`. План модели монтирования объявляется через `model` фабрики `slot`. Уточняет [D88](#d88), [D169](#d169), [D220](#d220), [D223](#d223).
|
package/docs/how-it-works.md
CHANGED
|
@@ -104,8 +104,11 @@ to these nodes:
|
|
|
104
104
|
|
|
105
105
|
`exports` is a factory of the form `({ own }) => …`. It runs when the instance opens, against a live `own`, and every
|
|
106
106
|
field of the record it returns must be a live value form — a `Call`, a `Readable` or a `Resource` — checked key by key
|
|
107
|
-
(D88). `provides` runs once, synchronously,
|
|
108
|
-
|
|
107
|
+
(D88). `provides` runs once, synchronously, with exactly `{ port, register, slot, pipe }`, and is split into ports and
|
|
108
|
+
contributions by the builder that produced each entry. Its outer context contains no `own` or instance values.
|
|
109
|
+
Every target remains known to the compiler before opening; a `port` selector is recorded for preparation, while
|
|
110
|
+
contribution values are recorded for publication (D255). Every attachment of a feature is critical: the instance is
|
|
111
|
+
not ready until all of them are.
|
|
109
112
|
|
|
110
113
|
### 2.2 Opening an instance
|
|
111
114
|
|
|
@@ -181,8 +184,13 @@ that.
|
|
|
181
184
|
|
|
182
185
|
The dependency descriptor is a map of authentic current-feature import and call refs. Materialization resolves
|
|
183
186
|
it once to a readonly map before calling the factory; optional imports stay lookup readables. The zero-dependency
|
|
184
|
-
form is `model(Decl, factory)`. Model-to-model refs are not a dependency graph.
|
|
185
|
-
call
|
|
187
|
+
form is `model(Decl, factory)`. Model-to-model refs are not a dependency graph. `port(Port, ({ own }) => own.model.call)`
|
|
188
|
+
or `port(Port, ({ own }) => own.call)` receives only `{ own }` and selects an authentic Call of the current instance
|
|
189
|
+
once during preparation, after model creation and before readiness or contribution publication. The binding wraps
|
|
190
|
+
even a passthrough call with the provider instance's fence (D169, D255).
|
|
191
|
+
The result must be a Call exposed by the `own` values the selector accessed. Reading only those values keeps
|
|
192
|
+
lifecycle-only entries lazy. The runtime checks currentness before and after each selector; retirement inside one
|
|
193
|
+
skips later selectors and drains the work already owned.
|
|
186
194
|
|
|
187
195
|
The kernel owns rollback before entering user code, so a factory that creates nodes and then throws still drains
|
|
188
196
|
them. The same rule holds for per-mount UI models. A fence synchronously closes every owned state and rejects
|
|
@@ -375,6 +383,13 @@ closing waits for the disposer and for the queue to drain.
|
|
|
375
383
|
| 5 | **after `ready`** the factories run with the instance context, the value is marked with its owner, and `when` is resolved: a predicate becomes one computed readable of that instance |
|
|
376
384
|
| 6 | every affected target is updated **in one transaction**, and listeners are called afterwards |
|
|
377
385
|
|
|
386
|
+
The factories receive only the capabilities needed by their target: `register` gets `{ own, imports }`, while
|
|
387
|
+
`slot` gets `{ own, imports, model }` for declaring per-mount UI models. The `own` record is the same lazy
|
|
388
|
+
materialization used by `exports`: model records, Calls and resources belong to the concrete instance, and
|
|
389
|
+
unselected lifecycle-only keys do not prevent access to them. A factory can therefore register its own model's Call without
|
|
390
|
+
making it part of the feature's public exports. `when` and `pipe.fold` use `{ own, imports, read }`; these
|
|
391
|
+
contribution contexts carry no `exports` or instance handle (D255).
|
|
392
|
+
|
|
378
393
|
Entry order: `priority` ascending, ties broken by id. `when` (a `Readable<boolean>`, or a predicate of the instance
|
|
379
394
|
that the runtime lowers to one): an entry whose value is `false`
|
|
380
395
|
is not part of `entries`, so `Slot`, `fold`, `select` and emptiness checks never see it; a flip publishes in one
|
|
@@ -402,7 +417,7 @@ include both the entry list and everything the handlers read through their `{ re
|
|
|
402
417
|
their sources change rather than only when contributions are re-registered. Without a reader the reads are untracked.
|
|
403
418
|
|
|
404
419
|
A handler is declared as a descriptor, `pipe(target, { fold })`, and the runtime binds one evaluation context to it
|
|
405
|
-
when the contribution publishes: `
|
|
420
|
+
when the contribution publishes: `imports` and the materialized `own` are resolved once and the reader is
|
|
406
421
|
the only thing that changes from call to call, so folding over many handlers allocates no per-handler context
|
|
407
422
|
(D223). A `fold` is the only thing that runs a handler: declaring, preloading and publishing do not.
|
|
408
423
|
|
package/docs/how-it-works.ru.md
CHANGED
|
@@ -99,8 +99,10 @@ Reconciler группы это один сериализованный worker с
|
|
|
99
99
|
| `requires.x` | один call на скрытом attachment-е требований, который пересылает в провайдера порта; у `optional(port)` он оседает `CallError` `unavailable`, пока провайдера нет (D105) |
|
|
100
100
|
| `model(...)` | **не узел kernel-а**, а описатель данных; фабрика модели здесь не выполняется; владеемое состояние живёт только в модели (`ctx.state`, D139) |
|
|
101
101
|
|
|
102
|
-
`exports` проверяется по форме каждого поля (`Call`, `Readable` или `Resource`), идентичность с `own` намеренно не проверяется; фасад экспортов вычисляется при открытии экземпляра с живым `own` (D88). `provides` выполняется один раз, синхронно, и делится на порты и вклады по тому,
|
|
103
|
-
какой builder породил запись.
|
|
102
|
+
`exports` проверяется по форме каждого поля (`Call`, `Readable` или `Resource`), идентичность с `own` намеренно не проверяется; фасад экспортов вычисляется при открытии экземпляра с живым `own` (D88). `provides` выполняется один раз, синхронно, с ровно `{ port, register, slot, pipe }` и делится на порты и вклады по тому,
|
|
103
|
+
какой builder породил запись. Во внешнем контексте нет `own` или значений экземпляра. Все цели остаются известны
|
|
104
|
+
компилятору до открытия: селектор `port` записывается для подготовки экземпляра, значения вкладов — для публикации
|
|
105
|
+
(D255). Каждый attachment фичи критичен: экземпляр не готов, пока не готовы все.
|
|
104
106
|
|
|
105
107
|
### 2.2 Открытие экземпляра
|
|
106
108
|
|
|
@@ -174,8 +176,13 @@ Retire идемпотентен и не может быть запущен из
|
|
|
174
176
|
|
|
175
177
|
Описатель зависимостей — запись подлинных импортов и call refs текущей фичи. Материализация один раз разрешает
|
|
176
178
|
её в readonly-запись перед фабрикой; optional-импорты остаются lookup-readable. Форма без зависимостей —
|
|
177
|
-
`model(Decl, factory)`. Refs моделей не образуют граф зависимостей.
|
|
178
|
-
|
|
179
|
+
`model(Decl, factory)`. Refs моделей не образуют граф зависимостей. `port(Port, ({ own }) => own.model.call)` или
|
|
180
|
+
`port(Port, ({ own }) => own.call)` получает только `{ own }` и один раз выбирает подлинный Call текущего экземпляра
|
|
181
|
+
при подготовке, после создания моделей и до readiness или публикации вкладов. Привязка оборачивает даже переданный
|
|
182
|
+
напрямую вызов фенсом экземпляра-провайдера (D169, D255).
|
|
183
|
+
Результат должен быть Call из значений `own`, к которым обратился селектор. Чтение только выбранных значений
|
|
184
|
+
сохраняет ленивость полей для lifecycle. Runtime проверяет актуальность до и после каждого селектора; retirement
|
|
185
|
+
внутри одного из них пропускает последующие селекторы и дренирует уже владеемую работу.
|
|
179
186
|
|
|
180
187
|
Kernel получает владельца отката до входа в пользовательский код, поэтому узлы фабрики дренируются, даже если
|
|
181
188
|
она после их создания бросила. То же правило держат per-mount UI-модели. Фенс синхронно закрывает каждое
|
|
@@ -365,6 +372,13 @@ disposer и слив очереди.
|
|
|
365
372
|
| 5 | **после `ready`** фабрики выполняются с контекстом экземпляра, значение помечается владельцем, `when` резолвится: предикат становится одним computed-readable этого экземпляра |
|
|
366
373
|
| 6 | все затронутые цели обновляются **в одной транзакции**, слушатели зовутся после |
|
|
367
374
|
|
|
375
|
+
Фабрики получают только возможности, нужные их цели: `register` — `{ own, imports }`, а `slot` —
|
|
376
|
+
`{ own, imports, model }` для объявления UI-моделей на каждое монтирование. Запись `own` использует ту же ленивую
|
|
377
|
+
материализацию, что `exports`: записи моделей, Calls и ресурсы принадлежат конкретному экземпляру, а невыбранные
|
|
378
|
+
поля только для lifecycle не мешают обращаться к ним. Поэтому фабрика может зарегистрировать Call своей модели без его
|
|
379
|
+
включения в публичные экспорты фичи. `when` и `pipe.fold` используют `{ own, imports, read }`; в этих контекстах
|
|
380
|
+
вкладов нет `exports` или handle экземпляра (D255).
|
|
381
|
+
|
|
368
382
|
Порядок записей: `priority` по возрастанию, при равенстве по id. `when` (`Readable<boolean>` либо предикат
|
|
369
383
|
экземпляра, который рантайм понижает в такой readable): запись с `false` не
|
|
370
384
|
входит в `entries`, поэтому `Slot`, `fold`, `select` и проверки пустоты её не видят; переключение публикует одной
|
|
@@ -391,7 +405,7 @@ disposer и слив очереди.
|
|
|
391
405
|
при смене их источников, а не только при перерегистрации вкладов. Без читателя чтение нетрекаемое.
|
|
392
406
|
|
|
393
407
|
Обработчик объявляется дескриптором, `pipe(target, { fold })`, и рантайм связывает с ним один контекст вычисления в
|
|
394
|
-
момент публикации вклада: `
|
|
408
|
+
момент публикации вклада: `imports` и материализованный `own` разрешаются один раз, от вызова к вызову
|
|
395
409
|
меняется только читатель, поэтому свёртка по многим обработчикам не аллоцирует контекст на обработчик (D223).
|
|
396
410
|
Обработчик запускает только свёртка: объявление, предзагрузка и публикация его не вызывают.
|
|
397
411
|
|
package/docs/releases.md
CHANGED
|
@@ -25,9 +25,9 @@ npx playwright install --with-deps chromium firefox webkit
|
|
|
25
25
|
npm run check
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
CI
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
CI uses the official Playwright image, pinned by version and digest to match the locked Playwright dependency.
|
|
29
|
+
Update the dependency, image tag and digest together. The local commands above install browsers and their platform
|
|
30
|
+
dependencies without requiring that image.
|
|
31
31
|
|
|
32
32
|
`ci:pack` creates five real tarballs and installs them in isolated consumers. It verifies all export entries,
|
|
33
33
|
NodeNext/Bundler declarations, renderer and headless paths, Lint without runtime packages, maps and shipped links. React/React DOM and their type packages are installed separately at `19.0.0` and at the contributor toolchain versions; both renderer/Devtools consumers also run on Node `20.19.0`.
|
|
@@ -37,27 +37,48 @@ finishes. `pack-local` prepares archives for development and does not mark them
|
|
|
37
37
|
The Git commit and archive checksums must match at publication. Do not edit or rebuild the packages after
|
|
38
38
|
acceptance. The publish script rejects an unaccepted manifest, another commit or a dirty checkout.
|
|
39
39
|
|
|
40
|
-
##
|
|
40
|
+
## Local release
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
publish the accepted set:
|
|
42
|
+
After the Version packages PR is merged, update a clean local `main` and run:
|
|
44
43
|
|
|
45
44
|
```sh
|
|
46
|
-
|
|
45
|
+
npm run release:local
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
The
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
The command derives the shared version from the five package manifests; verifies pinned Node and automatically
|
|
49
|
+
relaunches itself with the pinned npm when necessary; checks the canonical repository, clean `main`, exact
|
|
50
|
+
`origin/main` commit and absence of pending changesets; checks npm authentication and starts `npm login` only when
|
|
51
|
+
no session exists; installs dependencies and matching Playwright browsers; runs the complete acceptance; then asks
|
|
52
|
+
you to type the exact version before calling the protected publisher. It publishes stable versions under
|
|
53
|
+
`candidate` and prereleases under `next`.
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
The low-level publisher remains available for recovery of an interrupted publication from unchanged accepted
|
|
56
|
+
archives:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
node tooling/release/publish.mjs --version <exact-version>
|
|
60
|
+
```
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
## First release and authentication
|
|
63
|
+
|
|
64
|
+
Local publication authenticates interactively with `npm login` using an account allowed to publish in `@opetope`.
|
|
65
|
+
|
|
66
|
+
The script publishes exact archives with lifecycle scripts disabled. Release candidates use `next`; stable
|
|
67
|
+
versions first use `candidate`. It checks existing versions before writing and resumes a partial publication only
|
|
68
|
+
when the already-published integrity matches. It never overwrites versions or requests promotion to `latest`;
|
|
69
|
+
verify the actual registry tags separately.
|
|
70
|
+
|
|
71
|
+
A successful upload can precede registry availability while [npm scans the package](https://github.blog/changelog/2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata/).
|
|
72
|
+
The final verification retries metadata reads after temporary failures or `E404`, with one 20-minute deadline
|
|
73
|
+
and at most 100 reads for all five packages. Each read is bounded to 15 seconds or the remaining deadline.
|
|
74
|
+
Integrity mismatches, authentication failures and malformed responses stop verification immediately; publish
|
|
75
|
+
commands are never retried automatically. If verification times out after uploads succeeded, preserve the accepted
|
|
76
|
+
archives, check registry availability and rerun only after it recovers. Do not rebuild or change versions to work
|
|
77
|
+
around this delay. An existing matching version is skipped on the next run.
|
|
78
|
+
|
|
79
|
+
Pushes to `main` run the **Version packages** workflow, which only prepares the release PR. npm publication is local
|
|
80
|
+
through `npm run release:local`; the repository currently has no GitHub publish job, npm token or OIDC publishing
|
|
81
|
+
permission. Enable GitHub Actions' permission to create PRs in the repository settings.
|
|
61
82
|
|
|
62
83
|
## Registry acceptance and promotion
|
|
63
84
|
|
package/docs/releases.ru.md
CHANGED
|
@@ -25,9 +25,9 @@ npx playwright install --with-deps chromium firefox webkit
|
|
|
25
25
|
npm run check
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
CI
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
CI использует официальный образ Playwright, закреплённый по версии и digest в соответствии с версией Playwright в
|
|
29
|
+
lockfile. Обновляйте зависимость, тег образа и digest вместе. Локальные команды выше устанавливают браузеры и нужные
|
|
30
|
+
системе зависимости без необходимости использовать этот образ.
|
|
31
31
|
|
|
32
32
|
`ci:pack` создаёт пять настоящих tarballs и устанавливает их в изолированных потребителях. Проверяются все export
|
|
33
33
|
entries, типы NodeNext/Bundler, renderer и headless пути, Lint без runtime-пакетов, maps и ссылки документов. React/React DOM и их типы устанавливаются отдельно в версии `19.0.0` и версиях contributor toolchain; оба renderer/Devtools consumer также исполняются на Node `20.19.0`.
|
|
@@ -37,27 +37,47 @@ entries, типы NodeNext/Bundler, renderer и headless пути, Lint без r
|
|
|
37
37
|
При публикации должны совпасть Git commit и контрольные суммы. После приёмки не меняйте и не пересобирайте
|
|
38
38
|
пакеты. Скрипт отклоняет непринятый manifest, другой commit и грязное рабочее дерево.
|
|
39
39
|
|
|
40
|
-
##
|
|
40
|
+
## Локальный релиз
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
принятый набор:
|
|
42
|
+
После merge PR Version packages обновите чистый локальный `main` и запустите:
|
|
44
43
|
|
|
45
44
|
```sh
|
|
46
|
-
|
|
45
|
+
npm run release:local
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
Команда определяет общую версию по manifest пяти пакетов; проверяет закреплённый Node и при необходимости сама
|
|
49
|
+
перезапускается с закреплённым npm; проверяет канонический репозиторий, чистый `main`, точное совпадение с commit
|
|
50
|
+
`origin/main` и отсутствие ожидающих changesets; проверяет npm-аутентификацию и запускает `npm login` только при
|
|
51
|
+
отсутствии сессии; устанавливает зависимости и подходящие браузеры Playwright; выполняет полную приёмку; затем
|
|
52
|
+
просит ввести точную версию перед вызовом защищённого publisher. Stable-версии публикуются под `candidate`,
|
|
53
|
+
prerelease — под `next`.
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
Низкоуровневый publisher остаётся для восстановления прерванной публикации из неизменённых принятых архивов:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
node tooling/release/publish.mjs --version <exact-version>
|
|
59
|
+
```
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
## Первый выпуск и аутентификация
|
|
62
|
+
|
|
63
|
+
Локальная публикация интерактивно аутентифицируется через `npm login` под аккаунтом с правом публикации в `@opetope`.
|
|
64
|
+
|
|
65
|
+
Скрипт отправляет точные архивы с отключёнными lifecycle scripts. RC используют `next`, stable сначала использует
|
|
66
|
+
`candidate`. До записи скрипт проверяет существующие версии; частичную публикацию можно продолжить только при
|
|
67
|
+
совпадении integrity уже опубликованного содержимого. Версии не перезаписываются, продвижение в `latest` не
|
|
68
|
+
запрашивается; фактические теги registry проверяйте отдельно.
|
|
69
|
+
|
|
70
|
+
Успешная отправка может предшествовать доступности версии, пока [npm проверяет пакет](https://github.blog/changelog/2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata/).
|
|
71
|
+
Финальная проверка повторяет чтение метаданных после временных ошибок или `E404`: общий предел для пяти пакетов —
|
|
72
|
+
20 минут и 100 чтений. Каждое чтение ограничено 15 секундами или оставшимся временем. Несовпадение integrity,
|
|
73
|
+
ошибка аутентификации или некорректный ответ сразу останавливают проверку; команды публикации автоматически не
|
|
74
|
+
повторяются. Если после успешной отправки истекло время проверки, сохраните принятые архивы, проверьте доступность
|
|
75
|
+
registry и повторите запуск только после её восстановления. Не пересобирайте пакеты и не меняйте версии ради
|
|
76
|
+
обхода задержки. Уже опубликованная версия с совпадающим integrity будет пропущена при следующем запуске.
|
|
77
|
+
|
|
78
|
+
Push в `main` запускает workflow **Version packages**, который только готовит release PR. Публикация в npm выполняется
|
|
79
|
+
локально через `npm run release:local`; сейчас в репозитории нет GitHub publish job, npm-токена или разрешения на
|
|
80
|
+
OIDC-публикацию. В настройках репозитория разрешите GitHub Actions создавать PR.
|
|
61
81
|
|
|
62
82
|
## Приёмка registry и продвижение
|
|
63
83
|
|