@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.
Files changed (40) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +24 -13
  3. package/README.ru.md +26 -13
  4. package/dist/feature-authoring-types.d.ts +6 -10
  5. package/dist/feature-authoring.js +1 -1
  6. package/dist/feature-authoring.js.map +1 -1
  7. package/dist/feature-body.d.ts +1 -1
  8. package/dist/feature-body.js.map +1 -1
  9. package/dist/feature-contribution.d.ts +5 -3
  10. package/dist/feature-contribution.js +1 -1
  11. package/dist/feature-contribution.js.map +1 -1
  12. package/dist/feature-definition-api.d.ts +1 -1
  13. package/dist/feature-generation.js +1 -1
  14. package/dist/feature-generation.js.map +1 -1
  15. package/dist/feature-lazy-generation.d.ts +1 -1
  16. package/dist/feature-lazy-generation.js +1 -1
  17. package/dist/feature-lazy-generation.js.map +1 -1
  18. package/dist/feature-own-values.d.ts +10 -0
  19. package/dist/feature-own-values.js +2 -0
  20. package/dist/feature-own-values.js.map +1 -0
  21. package/dist/feature-port-binding.d.ts +2 -1
  22. package/dist/feature-port-binding.js +1 -1
  23. package/dist/feature-port-binding.js.map +1 -1
  24. package/dist/feature-port.d.ts +13 -23
  25. package/dist/feature-port.js +1 -1
  26. package/dist/feature-port.js.map +1 -1
  27. package/dist/public-module-types.d.ts +1 -1
  28. package/dist/public-module.d.ts +1 -1
  29. package/docs/agent-guide.md +11 -4
  30. package/docs/agent-guide.ru.md +11 -4
  31. package/docs/cookbook.md +9 -7
  32. package/docs/cookbook.ru.md +9 -7
  33. package/docs/decisions.md +15 -1
  34. package/docs/how-it-works.md +20 -5
  35. package/docs/how-it-works.ru.md +19 -5
  36. package/docs/releases.md +38 -17
  37. package/docs/releases.ru.md +37 -17
  38. package/docs/spec.md +31 -17
  39. package/docs/spec.ru.md +32 -18
  40. package/package.json +3 -3
@@ -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, ModuleCallRefIdentity, 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, };
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, };
@@ -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, ModuleCallRefIdentity, 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';
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';
@@ -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
- Declaration stages (`own` and outer `provides`) see refs. Instance stages (`exports`, nested contribution
48
- factories and model factories) receive materialized values. Compose model dependencies with a readonly map of
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, { from: own.model, select: value => value.call })` for a model's call. `calls(imports.x, keys)`
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
- `({ exports, imports, own, read }) => boolean`. Its `read` only records what the answer depends on: do not write,
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
@@ -41,10 +41,17 @@ policy?, within })`; данные по ключу → `resource`; поток →
41
41
  загружается при открытии экземпляра (D186, D207). `defineFeature.preload(feature)` подтягивает этот код заранее,
42
42
  ничего не открывая; для фичи без тела это успешный no-op (D208).
43
43
 
44
- Стадии декларации (`own` и внешняя `provides`) видят refs. Стадии экземпляра (`exports`, вложенные фабрики
45
- вкладов и фабрики моделей) получают материализованные значения. Зависимости модели собираются readonly-записью
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
- Для вызова модели используется `port(Port, { from: own.model, select: value => value.call })`.
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
- `({ exports, imports, own, read }) => boolean`. Его `read` только записывает, от чего зависит ответ: внутри нельзя
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, own }) => ({ … }),
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: ({ own, port }) => ({ catalog: port(resolveItemPort, own.resolveItem) }),
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 projection runs once before publication
184
- and the provider's lifetime fences the selected call even when it is passed through from a dependency (D169).
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: ({ own, port }) => ({
191
- submit: port(SubmitPort, { from: own.order, select: order => order.submit }),
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, ({ exports }) => ({ key: 'card', value: { submit: exports.submit, title: 'Card' } })),
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
@@ -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, own }) => ({ … }),
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: ({ own, port }) => ({ catalog: port(resolveItemPort, own.resolveItem) }),
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
- жизни провайдера ограничивает выбранный вызов, даже переданный напрямую из зависимости (D169).
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: ({ own, port }) => ({
190
- submit: port(SubmitPort, { from: own.order, select: order => order.submit }),
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, ({ exports }) => ({ key: 'card', value: { submit: exports.submit, title: 'Card' } })),
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
- у `event` появляется необязательная опция `backpressure` с тем же публичным словом `latest()`, которое требует `stream`: пока `run` исполняется и один payload ждёт, новый payload заменяет ожидающего и запись отказа не пишется. Дефолт без опции остаётся прежним: слот принадлежит payload, занявшему его первым, а новый `emit` отбрасывается разделяемой записью `queue-capacity`.
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).
@@ -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, and is split into ports and contributions by the builder that produced each
108
- entry. Every attachment of a feature is critical: the instance is not ready until all of them are.
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. A model-backed port selects its
185
- call once after model creation and wraps even a passthrough call with the provider instance's fence (D169).
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: `exports`, `imports` and the materialized `own` are resolved once and the reader is
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
 
@@ -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 породил запись. Каждый attachment фичи критичен: экземпляр не готов, пока не готовы все.
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
- создания модели и ограничивает фенсом экземпляра-провайдера даже переданный напрямую вызов (D169).
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
- момент публикации вклада: `exports`, `imports` и материализованный `own` разрешаются один раз, от вызова к вызову
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 and release acceptance use the official Playwright image, pinned by version and digest to match the locked
29
- Playwright dependency. Update the dependency, image tag and digest together. The local commands above install
30
- browsers and their platform dependencies without requiring that image.
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
- ## First release and authentication
40
+ ## Local release
41
41
 
42
- Authenticate interactively with `npm login` using an account allowed to publish in `@opetope`. For the first release,
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
- node tooling/release/publish.mjs --version 0.1.0
45
+ npm run release:local
47
46
  ```
48
47
 
49
- The script publishes exact archives with lifecycle scripts disabled. Release candidates use `next`; stable
50
- versions first use `candidate`. It checks existing versions before writing and resumes a partial publication only
51
- when the already-published integrity matches. It never overwrites versions or changes `latest`.
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
- After the first package versions exist, configure each package's npm trusted publisher with owner `telchardev`,
54
- repository `opetope`, workflow `release.yml`, and permission for direct `npm publish`. The workflow uses a
55
- GitHub-hosted runner and OIDC. No permanent npm write token is needed. This repository is private, so provenance
56
- is disabled. See [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
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
- For subsequent releases, dispatch **Release** from the reviewed `main` commit and provide its exact version. The
59
- workflow repeats acceptance and publishes those same archives. Pushes to `main` prepare release PRs; they do not
60
- publish arbitrary commits. Enable GitHub Actions' permission to create PRs in the repository settings.
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
 
@@ -25,9 +25,9 @@ npx playwright install --with-deps chromium firefox webkit
25
25
  npm run check
26
26
  ```
27
27
 
28
- CI и release acceptance используют официальный образ Playwright, закреплённый по версии и digest в соответствии
29
- с версией Playwright в lockfile. Обновляйте зависимость, тег образа и digest вместе. Локальные команды выше
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
- Выполните интерактивный `npm login` под аккаунтом с правом публикации в `@opetope`. Для первого выпуска опубликуйте
43
- принятый набор:
42
+ После merge PR Version packages обновите чистый локальный `main` и запустите:
44
43
 
45
44
  ```sh
46
- node tooling/release/publish.mjs --version 0.1.0
45
+ npm run release:local
47
46
  ```
48
47
 
49
- Скрипт отправляет точные архивы с отключёнными lifecycle scripts. RC используют `next`, stable сначала использует
50
- `candidate`. До записи скрипт проверяет существующие версии; частичную публикацию можно продолжить только при
51
- совпадении integrity уже опубликованного содержимого. Версии не перезаписываются, `latest` не меняется.
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
- После появления первых версий настройте для каждого пакета npm trusted publisher: owner `telchardev`, repository
54
- `opetope`, workflow `release.yml`, разрешение прямого `npm publish`. Workflow использует GitHub-hosted runner и
55
- OIDC без постоянного npm write token. Репозиторий приватный, поэтому provenance выключен.
56
- См. [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
55
+ Низкоуровневый publisher остаётся для восстановления прерванной публикации из неизменённых принятых архивов:
56
+
57
+ ```sh
58
+ node tooling/release/publish.mjs --version <exact-version>
59
+ ```
57
60
 
58
- Для последующих релизов запустите **Release** вручную из проверенного commit в `main`, указав точную версию.
59
- Workflow повторяет приёмку и публикует эти же архивы. Push в `main` готовит release PR, но не публикует произвольные
60
- commits. В настройках репозитория разрешите GitHub Actions создавать PR.
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