@astrale-os/sdk 0.5.0-beta.78 → 0.5.0-beta.79

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 (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/application/mutation/authoring/builder.d.ts +4 -5
  3. package/dist/application/mutation/authoring/builder.js +2 -2
  4. package/dist/application/mutation/authoring/endpoint.d.ts +1 -1
  5. package/dist/application/mutation/authoring/properties.d.ts +3 -3
  6. package/dist/application/mutation/authoring/transition.d.ts +14 -7
  7. package/dist/application/mutation/authoring/transition.js +15 -9
  8. package/dist/application/mutation/define.d.ts +1 -2
  9. package/dist/application/mutation/execution/execute.js +1 -1
  10. package/dist/application/state/index.d.ts +2 -2
  11. package/dist/application/state/index.js +1 -1
  12. package/dist/deployment/build/material.d.ts +0 -2
  13. package/dist/deployment/build/material.js +0 -50
  14. package/dist/index.d.ts +0 -2
  15. package/dist/index.js +0 -1
  16. package/dist/platform/schema/index.d.ts +0 -2
  17. package/dist/platform/schema/index.js +0 -1
  18. package/dist/tooling/linter/implementations/source/state-machine.js +1 -1
  19. package/dist/tooling/linter/implementations/source/states.js +1 -1
  20. package/package.json +6 -6
  21. package/dist/application/state/authority.d.ts +0 -3
  22. package/dist/application/state/authority.js +0 -7
  23. package/dist/application/state/define.d.ts +0 -6
  24. package/dist/application/state/define.js +0 -54
  25. package/dist/application/state/machine.d.ts +0 -55
  26. package/dist/application/state/machine.js +0 -1
  27. package/dist/application/state/relation.d.ts +0 -15
  28. package/dist/application/state/relation.js +0 -82
  29. package/dist/application/state/transition.d.ts +0 -22
  30. package/dist/application/state/transition.js +0 -1
  31. package/dist/platform/schema/state-property/authoring.d.ts +0 -14
  32. package/dist/platform/schema/state-property/authoring.js +0 -49
  33. package/dist/platform/schema/state-property/edge.d.ts +0 -16
  34. package/dist/platform/schema/state-property/edge.js +0 -21
  35. package/dist/platform/schema/state-property/index.d.ts +0 -4
  36. package/dist/platform/schema/state-property/index.js +0 -3
  37. package/dist/platform/schema/state-property/material.d.ts +0 -16
  38. package/dist/platform/schema/state-property/material.js +0 -118
  39. package/dist/platform/schema/state-property/model.d.ts +0 -49
  40. package/dist/platform/schema/state-property/model.js +0 -1
  41. package/dist/platform/schema/state-property/registry.d.ts +0 -12
  42. package/dist/platform/schema/state-property/registry.js +0 -85
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0-beta.79](https://github.com/astrale-os/sdk/compare/sdk-v0.5.0-beta.78...sdk-v0.5.0-beta.79) (2026-08-29)
4
+
5
+
6
+ ### Features
7
+
8
+ * **state:** consume portable DSL topology ([#333](https://github.com/astrale-os/sdk/issues/333)) ([7f8e255](https://github.com/astrale-os/sdk/commit/7f8e25574f3409baa03ae6fa015ee906572a2aee))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **release:** require publishable public changes ([#335](https://github.com/astrale-os/sdk/issues/335)) ([46755ee](https://github.com/astrale-os/sdk/commit/46755ee9124ab01a9e72158da0126e96c9410774))
14
+
3
15
  ## [0.5.0-beta.78](https://github.com/astrale-os/sdk/compare/sdk-v0.5.0-beta.77...sdk-v0.5.0-beta.78) (2026-08-29)
4
16
 
5
17
 
@@ -1,12 +1,11 @@
1
1
  import type { EdgeSlug } from '@astrale-os/kernel-core/graph/edge';
2
2
  import type { MutationBuilder } from '@astrale-os/kernel-core/graph/mutate';
3
3
  import type { QueryEdgeAST, QueryNodeAST } from '@astrale-os/kernel-core/graph/query';
4
- import type { Domain, ResolvedClass } from '../../../platform/schema/index.js';
5
- import type { EventOf, StateOf } from '../../state/machine.js';
4
+ import type { ResolvedClass } from '@astrale-os/kernel-dsl/v1';
6
5
  import type { CreatedNode, ExistingEndpoint, MutationEndpoint } from './endpoint.js';
7
6
  import type { QueryPrecondition } from './precondition.js';
8
7
  import type { RichPropertyConditions, RichPropertyDelta, RichPropertyInput } from './properties.js';
9
- import { type StateMachineOf, type StatePropertyName, type StateTransitionInput } from './transition.js';
8
+ import { type EventOfTopology, type StatePropertyName, type StatePropertyTopology, type StateOfTopology, type StateTransitionInput } from './transition.js';
10
9
  export interface RichMutationBuilder {
11
10
  readonly expect: {
12
11
  node<Class extends ResolvedClass<'node'>>(input: {
@@ -30,7 +29,7 @@ export interface RichMutationBuilder {
30
29
  readonly class: Class;
31
30
  readonly props: RichPropertyDelta<Class>;
32
31
  }): this;
33
- transition<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, Machine extends StateMachineOf<Class, Property>, From extends StateOf<Machine>, Event extends EventOf<Machine, From>>(input: StateTransitionInput<Class, Property, Machine, From, Event>): this;
32
+ transition<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, From extends StateOfTopology<StatePropertyTopology<Class, Property>>, Event extends EventOfTopology<StatePropertyTopology<Class, Property>, From>>(input: StateTransitionInput<Class, Property, From, Event>): this;
34
33
  deleteNode<Class extends ResolvedClass<'node'>>(input: {
35
34
  readonly node: ExistingEndpoint;
36
35
  readonly class: Class;
@@ -53,4 +52,4 @@ export interface EdgeSelector<Class extends ResolvedClass<'edge'>> {
53
52
  readonly target: ExistingEndpoint;
54
53
  readonly class: Class;
55
54
  }
56
- export declare function richMutationBuilder(core: MutationBuilder, domain: Domain): RichMutationBuilder;
55
+ export declare function richMutationBuilder(core: MutationBuilder): RichMutationBuilder;
@@ -4,7 +4,7 @@ import { endpoint, existingEndpoint } from './endpoint.js';
4
4
  import { precondition } from './precondition.js';
5
5
  import { conditions, delta, properties } from './properties.js';
6
6
  import { authorStateTransition, } from './transition.js';
7
- export function richMutationBuilder(core, domain) {
7
+ export function richMutationBuilder(core) {
8
8
  const expectation = Object.freeze({
9
9
  node(input) {
10
10
  core.expect.node({
@@ -48,7 +48,7 @@ export function richMutationBuilder(core, domain) {
48
48
  return rich;
49
49
  },
50
50
  transition(input) {
51
- authorStateTransition(core, domain, input);
51
+ authorStateTransition(core, input);
52
52
  return rich;
53
53
  },
54
54
  deleteNode(input) {
@@ -1,7 +1,7 @@
1
1
  import type { LocalBinding } from '@astrale-os/kernel-core/graph/graph';
2
2
  import type { NodeId } from '@astrale-os/kernel-core/graph/node';
3
3
  import type { NodeTarget } from '@astrale-os/kernel-core/path';
4
- import type { ResolvedClass } from '../../../platform/schema/index.js';
4
+ import type { ResolvedClass } from '@astrale-os/kernel-dsl/v1';
5
5
  export interface CreatedNode<Class extends ResolvedClass<'node'> = ResolvedClass<'node'>> {
6
6
  readonly created: LocalBinding;
7
7
  readonly class: Class;
@@ -1,5 +1,5 @@
1
1
  import type { MutationPropsInput } from '@astrale-os/kernel-core/graph/mutate';
2
- import type { PropertyInputOf, ResolvedClass, ResolvedProperty } from '../../../platform/schema/index.js';
2
+ import type { PropertyInputOf, ResolvedClass, ResolvedProperty } from '@astrale-os/kernel-dsl/v1';
3
3
  export type RichPropertyInput<Class> = PropertyInputOf<Class>;
4
4
  export type RichPropertyName<Class> = Extract<keyof PropertyInputOf<Class>, string>;
5
5
  export interface RichPropertyConditions<Class> {
@@ -12,11 +12,11 @@ export interface RichPropertyDelta<Class> {
12
12
  }
13
13
  export declare function properties<Class extends ResolvedClass>(selected: Class, input: RichPropertyInput<Class>): MutationPropsInput;
14
14
  export declare function conditions<Class extends ResolvedClass>(selected: Class, input: RichPropertyConditions<Class>): Readonly<{
15
- equals?: Readonly<Record<`${string}:class.${string}.property.${string}`, import("../../../platform/schema/index.js").Value>> | undefined;
15
+ equals?: Readonly<Record<`${string}:class.${string}.property.${string}`, import("@astrale-os/kernel-dsl/value").Value>> | undefined;
16
16
  absent?: readonly `${string}:class.${string}.property.${string}`[] | undefined;
17
17
  }>;
18
18
  export declare function delta<Class extends ResolvedClass>(selected: Class, input: RichPropertyDelta<Class>): Readonly<{
19
- set: Readonly<Record<`${string}:class.${string}.property.${string}`, import("../../../platform/schema/index.js").Value>>;
19
+ set: Readonly<Record<`${string}:class.${string}.property.${string}`, import("@astrale-os/kernel-dsl/value").Value>>;
20
20
  unset: readonly `${string}:class.${string}.property.${string}`[];
21
21
  }>;
22
22
  export declare function resolvedProperty<Class extends ResolvedClass>(selected: Class, name: RichPropertyName<Class>): ResolvedProperty;
@@ -1,12 +1,18 @@
1
1
  import type { MutationBuilder } from '@astrale-os/kernel-core/graph/mutate';
2
- import type { Domain, ResolvedClass } from '../../../platform/schema/index.js';
3
- import type { StatePropertiesOfClass } from '../../../platform/schema/state-property/index.js';
4
- import type { EventOf, StateMachine, StateOf } from '../../state/machine.js';
2
+ import type { ResolvedClass, ResolvedProperty } from '@astrale-os/kernel-dsl/v1';
3
+ import type { StateTopology } from '@astrale-os/kernel-dsl/v1/language/state';
5
4
  import type { ExistingEndpoint } from './endpoint.js';
6
5
  import type { RichPropertyConditions, RichPropertyDelta, RichPropertyName } from './properties.js';
7
- export type StatePropertyName<Class extends ResolvedClass<'node'>> = Extract<keyof StatePropertiesOfClass<Class>, RichPropertyName<Class>>;
8
- export type StateMachineOf<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>> = Extract<StatePropertiesOfClass<Class>[Property], StateMachine>;
9
- export interface StateTransitionInput<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, Machine extends StateMachineOf<Class, Property>, From extends StateOf<Machine>, Event extends EventOf<Machine, From>> {
6
+ type PropertyEntry<Class extends ResolvedClass<'node'>> = Class['properties']['byKey'][keyof Class['properties']['byKey']];
7
+ type TopologyOfProperty<Property> = Property extends ResolvedProperty<unknown, boolean, infer Topology extends StateTopology | undefined> ? Extract<Topology, StateTopology> : never;
8
+ type StatePropertyEntry<Property> = Property extends ResolvedProperty ? [TopologyOfProperty<Property>] extends [never] ? never : Property : never;
9
+ export type StatePropertyName<Class extends ResolvedClass<'node'>> = Extract<StatePropertyEntry<PropertyEntry<Class>>['name'], RichPropertyName<Class>>;
10
+ export type StatePropertyTopology<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>> = TopologyOfProperty<Extract<StatePropertyEntry<PropertyEntry<Class>>, {
11
+ readonly name: Property;
12
+ }>>;
13
+ export type StateOfTopology<Topology extends StateTopology> = Extract<keyof Topology['transitions'], string>;
14
+ export type EventOfTopology<Topology extends StateTopology, State extends StateOfTopology<Topology>> = State extends StateOfTopology<Topology> ? Extract<keyof Topology['transitions'][State], string> : never;
15
+ export interface StateTransitionInput<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, From extends StateOfTopology<StatePropertyTopology<Class, Property>>, Event extends EventOfTopology<StatePropertyTopology<Class, Property>, From>> {
10
16
  readonly node: ExistingEndpoint;
11
17
  readonly class: Class;
12
18
  readonly property: Property;
@@ -16,4 +22,5 @@ export interface StateTransitionInput<Class extends ResolvedClass<'node'>, Prope
16
22
  /** Auxiliary property changes committed in the same compare-and-set as the state transition. */
17
23
  readonly update?: RichPropertyDelta<Class>;
18
24
  }
19
- export declare function authorStateTransition<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, Machine extends StateMachineOf<Class, Property>, From extends StateOf<Machine>, Event extends EventOf<Machine, From>>(core: MutationBuilder, domain: Domain, input: StateTransitionInput<Class, Property, Machine, From, Event>): void;
25
+ export declare function authorStateTransition<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, From extends StateOfTopology<StatePropertyTopology<Class, Property>>, Event extends EventOfTopology<StatePropertyTopology<Class, Property>, From>>(core: MutationBuilder, input: StateTransitionInput<Class, Property, From, Event>): void;
26
+ export {};
@@ -1,9 +1,8 @@
1
1
  import { ClassKey } from '@astrale-os/kernel-dsl/v1/addressing';
2
- import { stateMachineFor } from '../../../platform/schema/state-property/registry.js';
3
2
  import { stateTransitionFailure } from '../failure.js';
4
3
  import { existingEndpoint } from './endpoint.js';
5
4
  import { conditions, delta, resolvedProperty } from './properties.js';
6
- export function authorStateTransition(core, domain, input) {
5
+ export function authorStateTransition(core, input) {
7
6
  const node = existingEndpoint(input.node);
8
7
  const selectedClass = input.class;
9
8
  const property = input.property;
@@ -21,15 +20,22 @@ export function authorStateTransition(core, domain, input) {
21
20
  const suppliedUnset = unset === undefined ? undefined : [...unset];
22
21
  const selected = resolvedProperty(selectedClass, property);
23
22
  const selectedClassKey = ClassKey(selectedClass.key);
24
- const machine = stateMachineFor(domain, selected);
25
- if (machine === undefined) {
26
- throw new TypeError(`State transition requires StateProperty ${selectedClassKey}.${String(property)} in the selected Domain.`);
23
+ const topology = selected.definition.state;
24
+ if (topology === undefined) {
25
+ throw new TypeError(`State transition requires StateProperty ${selectedClassKey}.${String(property)} on the selected Class.`);
27
26
  }
28
- const decision = machine.decide(fromState, event);
29
- if (decision.kind === 'rejected')
30
- throw stateTransitionFailure(decision.from, decision.event);
27
+ if (typeof fromState !== 'string' || !Object.hasOwn(topology.transitions, fromState)) {
28
+ throw new TypeError(`State machine does not declare state "${String(fromState)}".`);
29
+ }
30
+ if (typeof event !== 'string' ||
31
+ !Object.values(topology.transitions).some((events) => Object.hasOwn(events, event))) {
32
+ throw new TypeError(`State machine does not declare event "${String(event)}".`);
33
+ }
34
+ const target = topology.transitions[fromState]?.[event];
35
+ if (target === undefined)
36
+ throw stateTransitionFailure(fromState, event);
31
37
  const from = { [property]: fromState };
32
- const to = { [property]: decision.transition.to };
38
+ const to = { [property]: target };
33
39
  if ((suppliedEquals !== undefined &&
34
40
  Object.hasOwn(suppliedEquals, property) &&
35
41
  suppliedEquals[property] !== fromState) ||
@@ -1,9 +1,8 @@
1
1
  import type { MutationResult } from '@astrale-os/kernel-core/graph/mutate';
2
2
  import type { Domain, schema } from '../../platform/schema/index.js';
3
- import type { StatefulDomainOf } from '../../platform/schema/state-property/index.js';
4
3
  import type { Mutation, MutationDefinition } from './mutation.js';
5
4
  type ResolvedDomainOf<Schema extends schema.DomainSchema> = Extract<schema.DomainOf<Schema>, Domain>;
6
- type AuthoringDomainOf<Schema extends schema.DomainSchema> = StatefulDomainOf<Schema>;
5
+ type AuthoringDomainOf<Schema extends schema.DomainSchema> = ResolvedDomainOf<Schema>;
7
6
  interface MutationProjector<Schema extends schema.DomainSchema> {
8
7
  <Input, Output = MutationResult>(project: (domain: AuthoringDomainOf<Schema>) => MutationDefinition<Input, Output>): Mutation<ResolvedDomainOf<Schema>, Input, Output>;
9
8
  }
@@ -9,7 +9,7 @@ export async function executeMutation(client, domain, definition, input) {
9
9
  }
10
10
  const operation = realizeMutation(definition, domain);
11
11
  try {
12
- const ast = MutationAST.build((builder) => operation.build(input, richMutationBuilder(builder, domain)));
12
+ const ast = MutationAST.build((builder) => operation.build(input, richMutationBuilder(builder)));
13
13
  return projectMutation(operation, await client.mutate(ast, { expected: domain.closure }), input);
14
14
  }
15
15
  catch (cause) {
@@ -1,2 +1,2 @@
1
- export { stateMachine } from './define.js';
2
- export type { EventOf, InitialStateOf, NextStateOf, StateMachine, StateOf, TerminalStateOf, TransitionDecision, TransitionOf, } from './machine.js';
1
+ export { stateMachine } from '@astrale-os/kernel-dsl/v1/language/state';
2
+ export type { StateMachine, StateTopology, StateOf, EventOf, } from '@astrale-os/kernel-dsl/v1/language/state';
@@ -1 +1 @@
1
- export { stateMachine } from './define.js';
1
+ export { stateMachine } from '@astrale-os/kernel-dsl/v1/language/state';
@@ -1,6 +1,5 @@
1
1
  import type { DomainSchema } from '@astrale-os/kernel-dsl/v1/schema';
2
2
  import type { Runtime } from '../../application/runtime/index.js';
3
- import type { StatePropertyMaterial } from '../../platform/schema/state-property/material.js';
4
3
  import type { Build } from './build.js';
5
4
  import type { FrontendCompilation } from './frontend/index.js';
6
5
  export interface BuildMaterial {
@@ -14,7 +13,6 @@ export interface BuildMaterial {
14
13
  };
15
14
  };
16
15
  readonly callables: Build['callables'];
17
- readonly stateProperties: readonly StatePropertyMaterial[];
18
16
  readonly requirements: Build['requirements'];
19
17
  readonly routes: Build['routes'];
20
18
  readonly frontend?: FrontendCompilation;
@@ -8,18 +8,13 @@ import { BUNDLE_MEDIA_TYPE } from '@astrale-os/kernel-protocol/publication/bundl
8
8
  import { admitRequirements, validateRequirements, } from '@astrale-os/kernel-protocol/publication/requirements';
9
9
  import * as routes from '@astrale-os/kernel-protocol/routes';
10
10
  import { admitRuntimeRegistry, isRuntime } from '../../application/runtime/index.js';
11
- import { restoreStatePropertyMaterial, statePropertyMaterial, } from '../../platform/schema/state-property/material.js';
12
11
  import { admitBuild, isBuild } from './admission.js';
13
- import { evidenceOf } from './admission.js';
14
- const runtimeStateProperties = new WeakMap();
15
12
  /** Detach the provider-neutral, runtime-free material embedded into generated execution code. */
16
13
  export function material(build) {
17
14
  if (!isBuild(build))
18
15
  throw new TypeError('Build material requires an admitted Build.');
19
16
  if (!isRuntime(build.runtime))
20
17
  throw new TypeError('Build Runtime is not admitted.');
21
- const stateProperties = statePropertyMaterial(evidenceOf(build).domain);
22
- bindRuntimeStateProperties(build.runtime, build.schema.bundle.ref.digest, stateProperties);
23
18
  return Object.freeze({
24
19
  format: 'astrale.sdk.build-material',
25
20
  version: 1,
@@ -31,7 +26,6 @@ export function material(build) {
31
26
  }),
32
27
  }),
33
28
  callables: build.callables,
34
- stateProperties,
35
29
  requirements: build.requirements,
36
30
  routes: build.routes,
37
31
  ...(build.frontend === undefined ? {} : { frontend: build.frontend }),
@@ -61,9 +55,6 @@ export function restoreBuild(input, runtime) {
61
55
  revision(decoded.root) !== input.schema.compiled.root.revision) {
62
56
  invalid();
63
57
  }
64
- const stateProperties = snapshotStateProperties(input.stateProperties);
65
- assertRuntimeStateProperties(runtime, input.schema.bundle.ref.digest, stateProperties);
66
- restoreStatePropertyMaterial(domain, stateProperties);
67
58
  const registry = bindRuntime(domain, runtime, input.callables);
68
59
  const requirements = admitRequirements(input.requirements);
69
60
  validateRequirements(decoded.root, decoded.closure, requirements);
@@ -99,9 +90,6 @@ export function restoreGeneratedBuild(input, runtime) {
99
90
  const generated = acceptGeneratedMaterial(input, runtime);
100
91
  const material = generated.material;
101
92
  const domain = loadGeneratedSchema(material.schema.compiled);
102
- const stateProperties = snapshotStateProperties(material.stateProperties);
103
- restoreStatePropertyMaterial(domain, stateProperties);
104
- bindRuntimeStateProperties(runtime, material.schema.bundle.ref.digest, stateProperties);
105
93
  const registry = bindRuntime(domain, runtime, material.callables);
106
94
  const detached = Uint8Array.from(material.schema.bundle.bytes);
107
95
  const build = Object.freeze({
@@ -148,43 +136,6 @@ function digestOf(input) {
148
136
  invalid();
149
137
  }
150
138
  }
151
- function bindRuntimeStateProperties(runtime, bundleDigest, stateProperties) {
152
- const digest = statePropertyDigest(stateProperties);
153
- const bindings = runtimeStateProperties.get(runtime) ?? new Map();
154
- const existing = bindings.get(bundleDigest);
155
- if (existing !== undefined && existing !== digest) {
156
- throw new TypeError('Runtime has conflicting StateProperty authority for Build material.');
157
- }
158
- bindings.set(bundleDigest, digest);
159
- runtimeStateProperties.set(runtime, bindings);
160
- }
161
- function assertRuntimeStateProperties(runtime, bundleDigest, stateProperties) {
162
- const expected = runtimeStateProperties.get(runtime)?.get(bundleDigest);
163
- if (expected === undefined) {
164
- if (stateProperties.length === 0)
165
- return;
166
- throw new TypeError('Build StateProperty authority is not bound to its exact Runtime.');
167
- }
168
- if (statePropertyDigest(stateProperties) !== expected) {
169
- throw new TypeError('Build StateProperty authority differs from its exact Runtime.');
170
- }
171
- }
172
- function statePropertyDigest(input) {
173
- try {
174
- return artifact.identify(new TextEncoder().encode(JSON.stringify(input)), 'application/vnd.astrale.sdk.state-properties+json').digest;
175
- }
176
- catch {
177
- invalid();
178
- }
179
- }
180
- function snapshotStateProperties(input) {
181
- try {
182
- return deepFreeze(JSON.parse(JSON.stringify(input)));
183
- }
184
- catch {
185
- invalid();
186
- }
187
- }
188
139
  function assertMaterialEnvelope(input, runtime) {
189
140
  if (!isRuntime(runtime))
190
141
  throw new TypeError('Build Runtime is not admitted.');
@@ -197,7 +148,6 @@ function assertMaterialEnvelope(input, runtime) {
197
148
  key !== 'version' &&
198
149
  key !== 'schema' &&
199
150
  key !== 'callables' &&
200
- key !== 'stateProperties' &&
201
151
  key !== 'requirements' &&
202
152
  key !== 'routes' &&
203
153
  key !== 'frontend')) {
package/dist/index.d.ts CHANGED
@@ -11,8 +11,6 @@ export { defineCollectionQuery, defineCompositeQuery, defineQuery, executeQuery,
11
11
  export type { CollectionQueryDefinition, QueryDefinition, QueryFailure, } from './application/query/index.js';
12
12
  export { defineRuntime } from './application/runtime/index.js';
13
13
  export type { Runtime } from './application/runtime/index.js';
14
- export { stateMachine } from './application/state/index.js';
15
- export type { EventOf, StateMachine, StateOf, TransitionOf } from './application/state/index.js';
16
14
  export { defineWorkflow } from './application/workflow/index.js';
17
15
  export type { Workflow, WorkflowContext } from './application/workflow/index.js';
18
16
  export { compile, deploy, runtime } from './deployment/index.js';
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@ export { defineIntegration, defineProvider } from './application/integration/ind
5
5
  export { defineMutation, executeMutation } from './application/mutation/index.js';
6
6
  export { defineCollectionQuery, defineCompositeQuery, defineQuery, executeQuery, } from './application/query/index.js';
7
7
  export { defineRuntime } from './application/runtime/index.js';
8
- export { stateMachine } from './application/state/index.js';
9
8
  export { defineWorkflow } from './application/workflow/index.js';
10
9
  export { compile, deploy, runtime } from './deployment/index.js';
11
10
  export { serve } from './execution/index.js';
@@ -1,6 +1,4 @@
1
1
  import { KernelSchema as CoreKernelSchema } from '@astrale-os/kernel-core/schema';
2
- export { defineSchema, edgeClass, nodeClass, stateProperty } from './state-property/index.js';
3
- export type { StateClass, StateProperty, StateSchema } from './state-property/index.js';
4
2
  export * from '@astrale-os/kernel-dsl/v1';
5
3
  export { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
6
4
  export type { CallablePolicyBuilder, CallablePolicyContext, CallableProjection, AuthoredMethodProjection, AuthoredPropertyProjection, ClassIdentityProjection, ClassOwnerProjection, CoreEdge, CoreEndpoint, CoreNode, DefinedSchema, DirectedEdgeClass, ClassReferenceProjection, ClassProjection, FunctionProjection, EdgeClass, Function, Method, MethodProjection, Lazy, NodeClass, Policy, Property, PropertyProjection, ResolvedDomainOfInput, View, } from '@astrale-os/kernel-dsl/v1/builder';
@@ -1,7 +1,6 @@
1
1
  import { KernelSchema as CoreKernelSchema } from '@astrale-os/kernel-core/schema';
2
2
  /** Canonical Schema authoring, compilation, loading, and resolved Domain surface. */
3
3
  import { schema as language } from '@astrale-os/kernel-dsl/v1';
4
- export { defineSchema, edgeClass, nodeClass, stateProperty } from './state-property/index.js';
5
4
  export * from '@astrale-os/kernel-dsl/v1';
6
5
  export { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
7
6
  /** Kernel-owned Schema values used by authored product Schemas and graph operations. */
@@ -1,6 +1,6 @@
1
1
  import ts from 'typescript';
2
2
  import { resolveImportedSymbol, resolveProjectImport, staticText, unwrap, visit, } from '../../adapters/typescript/index.js';
3
- const STATE_MODULES = new Set(['@astrale-os/sdk', '@astrale-os/sdk/state']);
3
+ const STATE_MODULES = new Set(['@astrale-os/sdk/state']);
4
4
  const SCHEMA_MODULES = new Set([
5
5
  '@astrale-os/sdk',
6
6
  '@astrale-os/sdk/schema',
@@ -2,7 +2,7 @@ import ts from 'typescript';
2
2
  import { resolveImportedSymbol, resolveProjectImport, staticText, unwrap, visit, } from '../../adapters/typescript/index.js';
3
3
  import { ambiguity, forbiddenIoImport, hasForbiddenGlobalCall, production, violation, } from './shared.js';
4
4
  import { machineExportStatus, stateMachineConstructorOrigin } from './state-machine.js';
5
- const STATE_MODULES = new Set(['@astrale-os/sdk', '@astrale-os/sdk/state']);
5
+ const STATE_MODULES = new Set(['@astrale-os/sdk/state']);
6
6
  const FORBIDDEN_LAYERS = new Set([
7
7
  'actions',
8
8
  'integrations',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/sdk",
3
- "version": "0.5.0-beta.78",
3
+ "version": "0.5.0-beta.79",
4
4
  "description": "Schema-first SDK for defining, composing, and deploying Astrale domains",
5
5
  "keywords": [
6
6
  "astrale",
@@ -217,11 +217,11 @@
217
217
  "registry": "https://registry.npmjs.org/"
218
218
  },
219
219
  "dependencies": {
220
- "@astrale-os/kernel-client": "0.6.0-beta.30",
221
- "@astrale-os/kernel-core": "0.9.0-beta.23",
222
- "@astrale-os/kernel-dsl": "0.2.0-beta.17",
223
- "@astrale-os/kernel-protocol": "0.5.0-beta.24",
224
- "@astrale-os/kernel-server": "0.5.0-beta.26",
220
+ "@astrale-os/kernel-client": "0.6.0-beta.32",
221
+ "@astrale-os/kernel-core": "0.9.0-beta.25",
222
+ "@astrale-os/kernel-dsl": "0.2.0-beta.18",
223
+ "@astrale-os/kernel-protocol": "0.5.0-beta.26",
224
+ "@astrale-os/kernel-server": "0.5.0-beta.28",
225
225
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
226
226
  "hono": "^4.13.2",
227
227
  "jose": "^6.2.9",
@@ -1,3 +0,0 @@
1
- import type { StateMachine } from './machine.js';
2
- export declare function rememberStateMachine(machine: StateMachine): void;
3
- export declare function isStateMachine(input: unknown): input is StateMachine;
@@ -1,7 +0,0 @@
1
- const machines = new WeakSet();
2
- export function rememberStateMachine(machine) {
3
- machines.add(machine);
4
- }
5
- export function isStateMachine(input) {
6
- return typeof input === 'object' && input !== null && machines.has(input);
7
- }
@@ -1,6 +0,0 @@
1
- import type { StateMachine } from './machine.js';
2
- import type { StateName, TransitionRelation, ValidTransitionRelation } from './relation.js';
3
- export declare function stateMachine<const Transitions extends TransitionRelation, const Initial extends StateName<Transitions>>(input: Readonly<{
4
- initial: Initial;
5
- transitions: Transitions & ValidTransitionRelation<Transitions>;
6
- }>): StateMachine<Transitions, Initial>;
@@ -1,54 +0,0 @@
1
- import { z } from 'zod';
2
- import { rememberStateMachine } from './authority.js';
3
- import { relation } from './relation.js';
4
- export function stateMachine(input) {
5
- const admitted = relation(input);
6
- const states = admitted.states;
7
- const events = admitted.events;
8
- const transitions = states.flatMap((from) => Object.entries(admitted.transitions[from]).map(([event, to]) => Object.freeze({ from, event, to })));
9
- const terminalStates = states.filter((state) => Object.keys(admitted.transitions[state]).length === 0);
10
- const stateSchema = z.enum(states);
11
- const eventSchema = z.enum(events);
12
- const stateSet = new Set(states);
13
- const eventSet = new Set(events);
14
- const machine = Object.freeze({
15
- initial: admitted.initial,
16
- stateSchema,
17
- eventSchema,
18
- states: Object.freeze(states),
19
- events: Object.freeze(events),
20
- terminalStates: Object.freeze(terminalStates),
21
- transitions: Object.freeze(transitions),
22
- eventsFrom(state) {
23
- admitState(state, stateSet);
24
- return Object.freeze(Object.keys(admitted.transitions[state]));
25
- },
26
- decide(state, event) {
27
- admitState(state, stateSet);
28
- admitEvent(event, eventSet);
29
- const target = admitted.transitions[state][event];
30
- if (target === undefined) {
31
- return Object.freeze({
32
- kind: 'rejected',
33
- code: 'STATE_TRANSITION_ILLEGAL',
34
- from: state,
35
- event,
36
- });
37
- }
38
- return Object.freeze({
39
- kind: 'allowed',
40
- transition: Object.freeze({ from: state, event, to: target }),
41
- });
42
- },
43
- });
44
- rememberStateMachine(machine);
45
- return machine;
46
- }
47
- function admitState(state, states) {
48
- if (!states.has(state))
49
- throw new TypeError(`State machine does not declare state "${state}".`);
50
- }
51
- function admitEvent(event, events) {
52
- if (!events.has(event))
53
- throw new TypeError(`State machine does not declare event "${event}".`);
54
- }
@@ -1,55 +0,0 @@
1
- import type { z } from 'zod';
2
- import type { EventName, StateName, TransitionRelation } from './relation.js';
3
- import type { TransitionDecisionFor, TransitionWitness } from './transition.js';
4
- declare const stateMachineAuthority: unique symbol;
5
- export interface StateMachine<Transitions extends TransitionRelation = any, Initial extends StateName<Transitions> = StateName<Transitions>> extends StateMachineShape {
6
- /** Constructor provenance; not a runtime or portable field. */
7
- readonly [stateMachineAuthority]: void;
8
- readonly initial: Initial;
9
- readonly stateSchema: z.ZodType<StateName<Transitions>>;
10
- readonly eventSchema: z.ZodType<EventName<Transitions>>;
11
- readonly states: readonly StateName<Transitions>[];
12
- readonly events: readonly EventName<Transitions>[];
13
- readonly terminalStates: readonly TerminalState<Transitions>[];
14
- readonly transitions: readonly TransitionWitness<Transitions>[];
15
- eventsFrom<State extends StateName<Transitions>>(state: State): readonly EventName<Transitions, State>[];
16
- decide<State extends StateName<Transitions>, Event extends EventName<Transitions>>(state: State, event: Event): TransitionDecisionFor<Transitions, State, Event>;
17
- }
18
- interface StateMachineShape {
19
- readonly initial: string;
20
- readonly states: readonly string[];
21
- readonly events: readonly string[];
22
- readonly terminalStates: readonly string[];
23
- readonly transitions: readonly Readonly<{
24
- from: string;
25
- event: string;
26
- to: string;
27
- }>[];
28
- }
29
- export type StateOf<Machine extends StateMachine> = Machine['states'][number];
30
- export type EventOf<Machine extends StateMachine, State extends StateOf<Machine> = StateOf<Machine>> = Extract<TransitionOf<Machine>, {
31
- readonly from: State;
32
- }>['event'];
33
- export type NextStateOf<Machine extends StateMachine, State extends StateOf<Machine>, Event extends EventOf<Machine, State>> = Extract<TransitionOf<Machine>, {
34
- readonly from: State;
35
- readonly event: Event;
36
- }>['to'];
37
- export type InitialStateOf<Machine extends StateMachine> = Machine['initial'];
38
- export type TerminalStateOf<Machine extends StateMachine> = Machine['terminalStates'][number];
39
- export type TransitionOf<Machine extends StateMachine> = Machine['transitions'][number];
40
- export type TransitionDecision<Machine extends StateMachine, State extends StateOf<Machine>, Event extends Machine['events'][number]> = State extends StateOf<Machine> ? Event extends Machine['events'][number] ? Extract<TransitionOf<Machine>, {
41
- readonly from: State;
42
- readonly event: Event;
43
- }> extends infer Transition ? [Transition] extends [never] ? Readonly<{
44
- kind: 'rejected';
45
- code: 'STATE_TRANSITION_ILLEGAL';
46
- from: State;
47
- event: Event;
48
- }> : Readonly<{
49
- kind: 'allowed';
50
- transition: Transition;
51
- }> : never : never : never;
52
- type TerminalState<Transitions extends TransitionRelation> = {
53
- [State in StateName<Transitions>]: EventName<Transitions, State> extends never ? State : never;
54
- }[StateName<Transitions>];
55
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,15 +0,0 @@
1
- export type TransitionRelation = Readonly<Record<string, Readonly<Record<string, string>>>>;
2
- export type StateName<Transitions extends TransitionRelation> = Extract<keyof Transitions, string>;
3
- export type EventName<Transitions extends TransitionRelation, State extends StateName<Transitions> = StateName<Transitions>> = State extends StateName<Transitions> ? Extract<keyof Transitions[State], string> : never;
4
- export type ValidTransitionRelation<Transitions extends TransitionRelation> = Readonly<{
5
- [State in StateName<Transitions>]: Readonly<{
6
- [Event in keyof Transitions[State]]: Exclude<StateName<Transitions>, State>;
7
- }>;
8
- }>;
9
- export interface StateRelation {
10
- readonly initial: string;
11
- readonly transitions: Readonly<Record<string, Readonly<Record<string, string>>>>;
12
- readonly states: readonly string[];
13
- readonly events: readonly string[];
14
- }
15
- export declare function relation(input: unknown): StateRelation;
@@ -1,82 +0,0 @@
1
- export function relation(input) {
2
- if (!isRecord(input) || !exactKeys(input, ['initial', 'transitions']))
3
- invalidInput();
4
- const initial = input.initial;
5
- const authored = input.transitions;
6
- if (typeof initial !== 'string' || initial.length === 0 || !isRecord(authored))
7
- invalidInput();
8
- const states = Object.keys(authored);
9
- if (states.length === 0) {
10
- throw new TypeError('State machine requires at least one state.');
11
- }
12
- if (states.includes('')) {
13
- throw new TypeError('State machine has an empty state name.');
14
- }
15
- if (!states.includes(initial)) {
16
- throw new TypeError(`State machine initial state "${initial}" is not declared.`);
17
- }
18
- const stateSet = new Set(states);
19
- const events = new Set();
20
- const transitions = Object.create(null);
21
- let transitionCount = 0;
22
- for (const state of states) {
23
- const outgoing = authored[state];
24
- if (!isRecord(outgoing)) {
25
- throw new TypeError(`State "${state}" transitions must be an object.`);
26
- }
27
- const accepted = Object.create(null);
28
- for (const [event, target] of Object.entries(outgoing)) {
29
- if (event.length === 0) {
30
- throw new TypeError(`State "${state}" has an empty event name.`);
31
- }
32
- if (typeof target !== 'string' || !stateSet.has(target)) {
33
- throw new TypeError(`State "${state}" event "${event}" targets undeclared state "${String(target)}".`);
34
- }
35
- if (target === state) {
36
- throw new TypeError(`State "${state}" event "${event}" is a self-transition.`);
37
- }
38
- events.add(event);
39
- accepted[event] = target;
40
- transitionCount += 1;
41
- }
42
- transitions[state] = Object.freeze(accepted);
43
- }
44
- if (transitionCount === 0) {
45
- throw new TypeError('State machine requires at least one transition event.');
46
- }
47
- const reachable = new Set([initial]);
48
- const pending = [initial];
49
- while (pending.length > 0) {
50
- const state = pending.shift();
51
- for (const target of Object.values(transitions[state])) {
52
- if (reachable.has(target))
53
- continue;
54
- reachable.add(target);
55
- pending.push(target);
56
- }
57
- }
58
- for (const state of states) {
59
- if (!reachable.has(state)) {
60
- throw new TypeError(`State "${state}" is unreachable from initial "${initial}".`);
61
- }
62
- }
63
- return Object.freeze({
64
- initial,
65
- transitions: Object.freeze(transitions),
66
- states: Object.freeze(states),
67
- events: Object.freeze([...events].sort(compareText)),
68
- });
69
- }
70
- function invalidInput() {
71
- throw new TypeError('State machine input must contain exactly initial and transitions.');
72
- }
73
- function isRecord(input) {
74
- return input !== null && typeof input === 'object' && !Array.isArray(input);
75
- }
76
- function exactKeys(input, expected) {
77
- const keys = Reflect.ownKeys(input);
78
- return keys.length === expected.length && keys.every((key) => expected.includes(String(key)));
79
- }
80
- function compareText(left, right) {
81
- return left < right ? -1 : left > right ? 1 : 0;
82
- }
@@ -1,22 +0,0 @@
1
- import type { EventName, StateName, TransitionRelation } from './relation.js';
2
- export type TransitionWitness<Transitions extends TransitionRelation> = {
3
- [State in StateName<Transitions>]: {
4
- [Event in EventName<Transitions, State>]: Readonly<{
5
- from: State;
6
- event: Event;
7
- to: Extract<Transitions[State][Event], StateName<Transitions>>;
8
- }>;
9
- }[EventName<Transitions, State>];
10
- }[StateName<Transitions>];
11
- export type TransitionDecisionFor<Transitions extends TransitionRelation, State extends StateName<Transitions>, Event extends EventName<Transitions>> = State extends StateName<Transitions> ? Event extends EventName<Transitions> ? Extract<TransitionWitness<Transitions>, {
12
- readonly from: State;
13
- readonly event: Event;
14
- }> extends infer Transition ? [Transition] extends [never] ? Readonly<{
15
- kind: 'rejected';
16
- code: 'STATE_TRANSITION_ILLEGAL';
17
- from: State;
18
- event: Event;
19
- }> : Readonly<{
20
- kind: 'allowed';
21
- transition: Transition;
22
- }> : never : never : never;
@@ -1 +0,0 @@
1
- export {};
@@ -1,14 +0,0 @@
1
- import type { DefinedSchema, NodeClassInput, ResolvedDomainOfInput, SchemaInput as DomainSchemaInput } from '@astrale-os/kernel-dsl/v1/builder';
2
- import { nodeClass as languageNodeClass } from '@astrale-os/kernel-dsl/v1';
3
- import type { StateMachine } from '../../../application/state/machine.js';
4
- import type { StateClass, StateClassesOfInput, StateDependenciesOfInput, StatePropertiesOfConfig, StateProperty, StateSchema } from './model.js';
5
- interface StatePropertyOptions {
6
- readonly description?: string;
7
- }
8
- type RequiredStatePropertyOptions<Options extends StatePropertyOptions> = Readonly<Options & {
9
- readonly required: true;
10
- }>;
11
- export declare function stateProperty<const Machine extends StateMachine, const Options extends StatePropertyOptions = Record<never, never>>(machine: Machine, options?: Options & Record<Exclude<keyof Options, keyof StatePropertyOptions>, never>): StateProperty<Machine, RequiredStatePropertyOptions<Options>>;
12
- export declare function nodeClass<const Config extends NodeClassInput = Record<never, never>>(config?: Config & Record<Exclude<keyof Config, keyof NodeClassInput>, never>): ReturnType<typeof languageNodeClass<Config>> & StateClass<StatePropertiesOfConfig<Config>>;
13
- export declare function defineSchema<const Origin extends string, const Input extends DomainSchemaInput>(origin: Origin, input: Input): DefinedSchema<Origin, ResolvedDomainOfInput<Origin, Input>> & StateSchema<StateClassesOfInput<Input>, StateDependenciesOfInput<Input>>;
14
- export {};
@@ -1,49 +0,0 @@
1
- import { defineSchema as defineLanguageSchema, nodeClass as languageNodeClass, property, schema, } from '@astrale-os/kernel-dsl/v1';
2
- import { isStateMachine } from '../../../application/state/authority.js';
3
- import { rememberStateClass, rememberStateProperty, rememberStateSchema } from './registry.js';
4
- export function stateProperty(machine, options) {
5
- if (!isStateMachine(machine))
6
- throw new TypeError('stateProperty requires an SDK StateMachine.');
7
- if (options !== undefined &&
8
- (typeof options !== 'object' ||
9
- options === null ||
10
- Array.isArray(options) ||
11
- Reflect.ownKeys(options).some((key) => key !== 'description'))) {
12
- throw new TypeError('stateProperty options are description only.');
13
- }
14
- const value = property(machine.stateSchema, {
15
- ...options,
16
- required: true,
17
- });
18
- rememberStateProperty(value, machine);
19
- return value;
20
- }
21
- export function nodeClass(config) {
22
- const captured = captureNodeClassInput(config);
23
- const value = languageNodeClass(captured);
24
- rememberStateClass(value, captured?.properties);
25
- return value;
26
- }
27
- export function defineSchema(origin, input) {
28
- const captured = captureSchemaInput(input);
29
- const source = defineLanguageSchema(origin, captured);
30
- rememberStateSchema(source, captured, schema.resolve(source));
31
- return source;
32
- }
33
- function captureNodeClassInput(config) {
34
- if (config === undefined)
35
- return undefined;
36
- const captured = { ...config };
37
- return {
38
- ...captured,
39
- ...(captured.properties === undefined ? {} : { properties: { ...captured.properties } }),
40
- };
41
- }
42
- function captureSchemaInput(input) {
43
- const captured = { ...input };
44
- return {
45
- ...captured,
46
- ...(captured.dependencies === undefined ? {} : { dependencies: { ...captured.dependencies } }),
47
- ...(captured.classes === undefined ? {} : { classes: { ...captured.classes } }),
48
- };
49
- }
@@ -1,16 +0,0 @@
1
- import type { DirectedEdgeInput, UndirectedEdgeInput } from '@astrale-os/kernel-dsl/v1/builder';
2
- import { edgeClass as languageEdgeClass } from '@astrale-os/kernel-dsl/v1';
3
- import type { StateProperty } from './model.js';
4
- interface EdgeClassAuthoring {
5
- directed<const Input extends Omit<DirectedEdgeInput, 'orientation'>>(input: Parameters<typeof languageEdgeClass.directed<Input>>[0] & WithoutStateProperties<Input>): ReturnType<typeof languageEdgeClass.directed<Input>>;
6
- undirected<const Input extends Omit<UndirectedEdgeInput, 'orientation'>>(input: Parameters<typeof languageEdgeClass.undirected<Input>>[0] & WithoutStateProperties<Input>): ReturnType<typeof languageEdgeClass.undirected<Input>>;
7
- }
8
- type WithoutStateProperties<Input> = Input extends {
9
- readonly properties?: infer Properties extends Readonly<Record<string, unknown>>;
10
- } ? {
11
- readonly properties?: {
12
- readonly [Name in keyof Properties]: Properties[Name] extends StateProperty ? never : Properties[Name];
13
- };
14
- } : unknown;
15
- export declare const edgeClass: Readonly<EdgeClassAuthoring>;
16
- export {};
@@ -1,21 +0,0 @@
1
- import { edgeClass as languageEdgeClass } from '@astrale-os/kernel-dsl/v1';
2
- import { isStateProperty } from './registry.js';
3
- export const edgeClass = Object.freeze({
4
- directed(input) {
5
- return languageEdgeClass.directed(captureEdgeInput(input));
6
- },
7
- undirected(input) {
8
- return languageEdgeClass.undirected(captureEdgeInput(input));
9
- },
10
- });
11
- function captureEdgeInput(input) {
12
- const captured = { ...input };
13
- const properties = captured.properties === undefined ? undefined : { ...captured.properties };
14
- if (Object.values(properties ?? {}).some(isStateProperty)) {
15
- throw new TypeError('StateProperty is supported only on Node Classes.');
16
- }
17
- return {
18
- ...captured,
19
- ...(properties === undefined ? {} : { properties }),
20
- };
21
- }
@@ -1,4 +0,0 @@
1
- export { defineSchema, nodeClass, stateProperty } from './authoring.js';
2
- export { edgeClass } from './edge.js';
3
- export type { StateClass, StatePropertiesOfClass, StateProperty, StateSchema, StatefulDomainOf, } from './model.js';
4
- export { stateMachineFor } from './registry.js';
@@ -1,3 +0,0 @@
1
- export { defineSchema, nodeClass, stateProperty } from './authoring.js';
2
- export { edgeClass } from './edge.js';
3
- export { stateMachineFor } from './registry.js';
@@ -1,16 +0,0 @@
1
- import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
- import type { PropertyKey } from '@astrale-os/kernel-dsl/v1/addressing';
3
- export interface StatePropertyMaterial {
4
- readonly property: PropertyKey;
5
- readonly initial: string;
6
- readonly states: readonly string[];
7
- readonly transitions: readonly Readonly<{
8
- from: string;
9
- event: string;
10
- to: string;
11
- }>[];
12
- }
13
- /** Capture only the portable StateProperty authority required by generated execution. */
14
- export declare function statePropertyMaterial(domain: Domain): readonly StatePropertyMaterial[];
15
- /** Restore portable StateProperty authority onto an exact compiled Domain. */
16
- export declare function restoreStatePropertyMaterial(domain: Domain, input: unknown): void;
@@ -1,118 +0,0 @@
1
- import { stateMachine } from '../../../application/state/define.js';
2
- import { rememberResolvedStateProperties, stateMachineFor } from './registry.js';
3
- /** Capture only the portable StateProperty authority required by generated execution. */
4
- export function statePropertyMaterial(domain) {
5
- const material = [];
6
- for (const property of ownProperties(domain)) {
7
- const machine = stateMachineFor(domain, property);
8
- if (machine === undefined)
9
- continue;
10
- material.push(Object.freeze({
11
- property: property.key,
12
- initial: machine.initial,
13
- states: Object.freeze([...machine.states]),
14
- transitions: Object.freeze(machine.transitions.map((transition) => Object.freeze({ ...transition }))),
15
- }));
16
- }
17
- return Object.freeze(material.sort((left, right) => compareText(left.property, right.property)));
18
- }
19
- /** Restore portable StateProperty authority onto an exact compiled Domain. */
20
- export function restoreStatePropertyMaterial(domain, input) {
21
- if (!Array.isArray(input))
22
- invalid();
23
- const byKey = new Map();
24
- const properties = new Map(ownProperties(domain).map((property) => [property.key, property]));
25
- for (const candidate of input) {
26
- if (!isRecord(candidate) ||
27
- !exactKeys(candidate, ['property', 'initial', 'states', 'transitions'])) {
28
- invalid();
29
- }
30
- const propertyKey = candidate.property;
31
- const initial = candidate.initial;
32
- const states = candidate.states;
33
- const transitions = candidate.transitions;
34
- if (typeof propertyKey !== 'string' ||
35
- typeof initial !== 'string' ||
36
- !Array.isArray(states) ||
37
- states.some((state) => typeof state !== 'string') ||
38
- !Array.isArray(transitions)) {
39
- invalid();
40
- }
41
- const property = properties.get(propertyKey);
42
- if (property === undefined || byKey.has(property.key))
43
- invalid();
44
- assertStateProperty(property, states);
45
- const relation = Object.create(null);
46
- for (const state of states)
47
- relation[state] = Object.create(null);
48
- for (const transition of transitions) {
49
- if (!isRecord(transition) || !exactKeys(transition, ['from', 'event', 'to']))
50
- invalid();
51
- const { from, event, to } = transition;
52
- if (typeof from !== 'string' ||
53
- typeof event !== 'string' ||
54
- typeof to !== 'string' ||
55
- relation[from] === undefined ||
56
- Object.hasOwn(relation[from], event)) {
57
- invalid();
58
- }
59
- relation[from][event] = to;
60
- }
61
- const machine = Reflect.apply(stateMachine, undefined, [
62
- { initial, transitions: relation },
63
- ]);
64
- if (!sameStrings(machine.states, states))
65
- invalid();
66
- byKey.set(property.key, machine);
67
- }
68
- rememberResolvedStateProperties(domain, byKey);
69
- }
70
- function ownProperties(domain) {
71
- const properties = [];
72
- const seen = new Set();
73
- const visit = (selected) => {
74
- if (seen.has(selected))
75
- return;
76
- seen.add(selected);
77
- for (const definition of selected.definitions('class')) {
78
- properties.push(...definition.self.properties);
79
- }
80
- for (const dependency of Object.values(selected.dependencies))
81
- visit(dependency);
82
- };
83
- visit(domain);
84
- return properties;
85
- }
86
- function assertStateProperty(property, states) {
87
- const schema = property.definition.schema;
88
- if (property.owner.kind !== 'node' ||
89
- property.definition.required !== true ||
90
- !isRecord(schema) ||
91
- schema.type !== 'string' ||
92
- !Array.isArray(schema.enum) ||
93
- schema.enum.some((state) => typeof state !== 'string') ||
94
- !sameStringSet(schema.enum, states)) {
95
- invalid();
96
- }
97
- }
98
- function sameStrings(left, right) {
99
- return left.length === right.length && left.every((value, index) => value === right[index]);
100
- }
101
- function sameStringSet(left, right) {
102
- if (new Set(left).size !== left.length || new Set(right).size !== right.length)
103
- return false;
104
- return sameStrings([...left].sort(compareText), [...right].sort(compareText));
105
- }
106
- function isRecord(input) {
107
- return input !== null && typeof input === 'object' && !Array.isArray(input);
108
- }
109
- function exactKeys(input, expected) {
110
- const keys = Reflect.ownKeys(input);
111
- return keys.length === expected.length && keys.every((key) => expected.includes(String(key)));
112
- }
113
- function compareText(left, right) {
114
- return left < right ? -1 : left > right ? 1 : 0;
115
- }
116
- function invalid() {
117
- throw new TypeError('StateProperty material is invalid.');
118
- }
@@ -1,49 +0,0 @@
1
- import type { Domain, ResolvedClass } from '@astrale-os/kernel-dsl/v1';
2
- import type { NodeClassInput, Property, PropertyOptions, SchemaInput as DomainSchemaInput } from '@astrale-os/kernel-dsl/v1/builder';
3
- import type * as languageSchema from '@astrale-os/kernel-dsl/v1/schema';
4
- import type { StateMachine } from '../../../application/state/machine.js';
5
- declare const STATE_PROPERTY_MACHINE: unique symbol;
6
- declare const STATE_CLASS_PROPERTIES: unique symbol;
7
- declare const STATE_SCHEMA_CLASSES: unique symbol;
8
- export interface StateProperty<Machine extends StateMachine = StateMachine, Options extends PropertyOptions = PropertyOptions> extends Property<Machine['stateSchema'], Options> {
9
- readonly [STATE_PROPERTY_MACHINE]: Machine;
10
- }
11
- export interface StateClass<Properties extends Readonly<Record<string, StateMachine>>> {
12
- readonly [STATE_CLASS_PROPERTIES]: Properties;
13
- }
14
- export interface StateSchema<Classes extends Readonly<Record<string, Readonly<Record<string, StateMachine>>>>, Dependencies extends Readonly<Record<string, languageSchema.DomainSchema>> = Readonly<Record<never, never>>> {
15
- readonly [STATE_SCHEMA_CLASSES]: {
16
- readonly classes: Classes;
17
- readonly dependencies: Dependencies;
18
- };
19
- }
20
- export type StatePropertiesOfConfig<Config extends NodeClassInput> = Merge<ParentStateProperties<Config>, OwnStateProperties<Config>>;
21
- export type StateClassesOfInput<Input extends DomainSchemaInput> = Input extends {
22
- readonly classes?: infer Classes extends Readonly<Record<string, unknown>>;
23
- } ? {
24
- readonly [Name in keyof Classes]: Classes[Name] extends StateClass<infer Properties> ? Properties : Readonly<Record<never, never>>;
25
- } : Readonly<Record<never, never>>;
26
- export type StateDependenciesOfInput<Input extends DomainSchemaInput> = Input extends {
27
- readonly dependencies?: infer Dependencies extends Readonly<Record<string, languageSchema.DomainSchema>>;
28
- } ? Dependencies : Readonly<Record<never, never>>;
29
- export type StatefulDomainOf<Schema extends languageSchema.DomainSchema> = Schema extends StateSchema<infer Classes, infer Dependencies> ? Extract<languageSchema.DomainOf<Schema>, Domain> extends infer DomainValue extends Domain ? DomainValue & {
30
- readonly classes: {
31
- readonly [Name in keyof DomainValue['classes']]: DomainValue['classes'][Name] & StateClass<Name extends keyof Classes ? Extract<Classes[Name], Readonly<Record<string, StateMachine>>> : Readonly<Record<never, never>>>;
32
- };
33
- readonly dependencies: {
34
- readonly [Name in keyof DomainValue['dependencies']]: Name extends keyof Dependencies ? Dependencies[Name] extends languageSchema.DomainSchema ? StatefulDomainOf<Dependencies[Name]> : DomainValue['dependencies'][Name] : DomainValue['dependencies'][Name];
35
- };
36
- } : never : Extract<languageSchema.DomainOf<Schema>, Domain>;
37
- export type StatePropertiesOfClass<Class extends ResolvedClass<'node'>> = Class extends StateClass<infer Properties> ? Properties : Readonly<Record<never, never>>;
38
- type OwnStateProperties<Config extends NodeClassInput> = Config extends {
39
- readonly properties?: infer Properties extends Readonly<Record<string, unknown>>;
40
- } ? {
41
- readonly [Name in keyof Properties as Properties[Name] extends StateProperty ? Name : never]: Properties[Name] extends StateProperty<infer Machine> ? Machine : never;
42
- } : Readonly<Record<never, never>>;
43
- type ParentStateProperties<Config extends NodeClassInput> = Config extends {
44
- readonly extends?: readonly (infer Parent)[];
45
- } ? UnionToIntersection<StatePropertiesOfReference<Parent>> : Readonly<Record<never, never>>;
46
- type StatePropertiesOfReference<Value, Depth extends readonly unknown[] = []> = Depth['length'] extends 32 ? Readonly<Record<never, never>> : Value extends () => infer Resolved ? StatePropertiesOfReference<Resolved, readonly [...Depth, unknown]> : Value extends StateClass<infer Properties> ? Properties : Readonly<Record<never, never>>;
47
- type UnionToIntersection<Value> = (Value extends unknown ? (input: Value) => void : never) extends (input: infer Intersection) => void ? Intersection : never;
48
- type Merge<Parent, Own> = Readonly<Omit<Parent, keyof Own> & Own>;
49
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,12 +0,0 @@
1
- import type { Domain, ResolvedProperty } from '@astrale-os/kernel-dsl/v1';
2
- import type { PropertyKey } from '@astrale-os/kernel-dsl/v1/addressing';
3
- import type { Property, SchemaInput as DomainSchemaInput } from '@astrale-os/kernel-dsl/v1/builder';
4
- import type * as languageSchema from '@astrale-os/kernel-dsl/v1/schema';
5
- import type { StateMachine } from '../../../application/state/machine.js';
6
- export declare function rememberStateProperty(property: Property, machine: StateMachine): void;
7
- export declare function isStateProperty(value: unknown): boolean;
8
- export declare function rememberStateClass(selected: object, properties?: Readonly<Record<string, unknown>>): void;
9
- export declare function rememberStateSchema(source: languageSchema.DomainSchema, input: DomainSchemaInput, domain: Domain): void;
10
- export declare function stateMachineFor(domain: Domain, selected: ResolvedProperty<unknown>): StateMachine | undefined;
11
- /** Internal restoration seam for generated execution material. */
12
- export declare function rememberResolvedStateProperties(domain: Domain, byKey: ReadonlyMap<PropertyKey, StateMachine>): void;
@@ -1,85 +0,0 @@
1
- const propertyMachines = new WeakMap();
2
- const classProperties = new WeakMap();
3
- const schemaProperties = new WeakMap();
4
- const domainProperties = new WeakMap();
5
- export function rememberStateProperty(property, machine) {
6
- propertyMachines.set(property, machine);
7
- }
8
- export function isStateProperty(value) {
9
- return typeof value === 'object' && value !== null && propertyMachines.has(value);
10
- }
11
- export function rememberStateClass(selected, properties) {
12
- const machines = new Map();
13
- for (const [name, property] of Object.entries(properties ?? {})) {
14
- if (typeof property !== 'object' || property === null)
15
- continue;
16
- const machine = propertyMachines.get(property);
17
- if (machine !== undefined)
18
- machines.set(name, machine);
19
- }
20
- classProperties.set(selected, machines);
21
- }
22
- export function rememberStateSchema(source, input, domain) {
23
- const byKey = new Map();
24
- for (const dependency of Object.values(input.dependencies ?? {})) {
25
- const associated = schemaProperties.get(dependency);
26
- if (associated === undefined)
27
- continue;
28
- for (const [key, machine] of associated.byKey)
29
- bindKey(byKey, key, machine);
30
- }
31
- for (const [className, authored] of Object.entries(input.classes ?? {})) {
32
- const machines = classProperties.get(authored);
33
- if (machines === undefined || machines.size === 0)
34
- continue;
35
- const resolved = Reflect.get(domain.classes, className);
36
- if (resolved === undefined)
37
- continue;
38
- for (const [propertyName, machine] of machines) {
39
- const property = Reflect.get(resolved.self.properties, propertyName);
40
- if (property !== undefined)
41
- bindKey(byKey, property.key, machine);
42
- }
43
- }
44
- const byProperty = new WeakMap();
45
- bindDomainProperties(domain, byKey, byProperty, new Set());
46
- schemaProperties.set(source, { byKey, byProperty });
47
- domainProperties.set(domain, byProperty);
48
- }
49
- export function stateMachineFor(domain, selected) {
50
- const retained = domainProperties.get(domain)?.get(selected);
51
- if (retained !== undefined)
52
- return retained;
53
- const source = Reflect.get(domain, 'source');
54
- if (typeof source !== 'object' || source === null)
55
- return undefined;
56
- return schemaProperties.get(source)?.byProperty.get(selected);
57
- }
58
- /** Internal restoration seam for generated execution material. */
59
- export function rememberResolvedStateProperties(domain, byKey) {
60
- const byProperty = new WeakMap();
61
- bindDomainProperties(domain, byKey, byProperty, new Set());
62
- domainProperties.set(domain, byProperty);
63
- }
64
- function bindKey(byKey, key, machine) {
65
- const existing = byKey.get(key);
66
- if (existing !== undefined && existing !== machine) {
67
- throw new TypeError(`StateProperty ${key} has more than one StateMachine authority.`);
68
- }
69
- byKey.set(key, machine);
70
- }
71
- function bindDomainProperties(domain, byKey, byProperty, seen) {
72
- if (seen.has(domain))
73
- return;
74
- seen.add(domain);
75
- for (const definition of domain.definitions('class')) {
76
- for (const property of definition.self.properties) {
77
- const machine = byKey.get(property.key);
78
- if (machine !== undefined)
79
- byProperty.set(property, machine);
80
- }
81
- }
82
- for (const dependency of Object.values(domain.dependencies)) {
83
- bindDomainProperties(dependency, byKey, byProperty, seen);
84
- }
85
- }