@astrale-os/sdk 0.6.0-beta.7 → 0.6.0-beta.9

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.
@@ -1,5 +1,5 @@
1
1
  export { defineAction } from './define.js';
2
2
  export type { Action, ActionDefinition } from './action.js';
3
- export type { ActionContext, ActionExecution, Caller, DomainGraphExecutor, DomainGraphExecutors, DomainKernelSessions, } from './context.js';
3
+ export type { ActionContext, ActionBinaryInput, ActionExecution, Caller, DomainGraphExecutor, DomainGraphExecutors, DomainKernelSessions, } from './context.js';
4
4
  export type { ActionBinary, ActionOutputOf } from './output.js';
5
5
  export type { DeclaredErrorDefinition, DeclaredErrorFactory, DeclaredErrorFamily, DeclaredErrorOptions, DeclaredErrors, } from '../error/index.js';
@@ -1,7 +1,7 @@
1
1
  import type { AuthenticationEvidence, Grant, IdentityId } from '../../platform/auth/index.js';
2
2
  import type { BoundClientSession } from '../../platform/client/session/index.js';
3
3
  import type { NodeId } from '../../platform/graph/node/index.js';
4
- import type { CallableInputOf, Domain, ResolvedFunction, ResolvedMethod } from '../../platform/schema/index.js';
4
+ import type { CallableInputOf, CallableInputModeOf, Domain, ResolvedFunction, ResolvedMethod } from '../../platform/schema/index.js';
5
5
  import type { HandlerDependencies } from '../dependency/index.js';
6
6
  import type { DeclaredErrorFactory, DeclaredErrors, NoDeclaredErrors } from '../error/index.js';
7
7
  import type { IntegrationClients, Integrations } from '../integration/index.js';
@@ -34,6 +34,16 @@ export interface ActionExecution {
34
34
  body(): Uint8Array;
35
35
  };
36
36
  }
37
+ /** One admitted, single-consumer binary Function request body. */
38
+ export interface ActionBinaryInput {
39
+ readonly body: ReadableStream<Uint8Array>;
40
+ readonly length: number;
41
+ }
42
+ type BinaryInputOf<Callable> = CallableInputModeOf<Callable> extends 'binary' ? {
43
+ readonly body: ActionBinaryInput;
44
+ } : {
45
+ readonly body?: never;
46
+ };
37
47
  export interface DomainGraphExecutor<DomainValue extends Domain = Domain> {
38
48
  readonly query: QueryExecutor<DomainValue>;
39
49
  readonly mutate: MutationExecutor<DomainValue>;
@@ -83,5 +93,5 @@ export type ActionContext<Callable extends ResolvedCallable, DomainValue extends
83
93
  readonly execution: ActionExecution;
84
94
  /** Construct one implementation-declared safe business error for `throw error(...)`. */
85
95
  readonly error: DeclaredErrorFactory<Errors>;
86
- } & AuthorityOf<Callable, DomainValue> & SelfOf<Callable>;
96
+ } & AuthorityOf<Callable, DomainValue> & SelfOf<Callable> & BinaryInputOf<Callable>;
87
97
  export {};
@@ -1,5 +1,5 @@
1
1
  export { defineAction, isAction } from './define.js';
2
2
  export type { Action, ActionAddress, ActionAddressOf, ActionDefinition, CallableAtAddress, LocalCallable, ResolvedDomainOf, } from './action.js';
3
- export type { ActionContext, ActionExecution, AnonymousCaller, AuthenticatedCaller, Caller, DomainGraphExecutor, DomainGraphExecutors, DomainKernelSessions, ResolvedCallable, } from './context.js';
3
+ export type { ActionContext, ActionBinaryInput, ActionExecution, AnonymousCaller, AuthenticatedCaller, Caller, DomainGraphExecutor, DomainGraphExecutors, DomainKernelSessions, ResolvedCallable, } from './context.js';
4
4
  export type { ActionBinary, ActionOutputOf } from './output.js';
5
5
  export type { DeclaredErrorDefinition, DeclaredErrorFactory, DeclaredErrorFamily, DeclaredErrorOptions, DeclaredErrors, } from '../error/index.js';
@@ -1 +1 @@
1
- export type { DependencyCallOptions, DependencyInvoker, HandlerDependencies } from './invoker.js';
1
+ export type { DependencyBinaryCallOptions, DependencyCallOptions, DependencyInvoker, HandlerDependencies, } from './invoker.js';
@@ -1,18 +1,23 @@
1
1
  import type { Binary, Input } from '../../platform/client/index.js';
2
- import type { SessionRequestOptions } from '../../platform/client/session/index.js';
2
+ import type { SessionRequestOptions, SessionRequestOptionsWithBody } from '../../platform/client/session/index.js';
3
3
  import type { NodeId } from '../../platform/graph/node/index.js';
4
- import type { CallableInputOf, CallableOutputOf, CallableResultOf, Domain, ResolvedMethod, KernelSchema, schema } from '../../platform/schema/index.js';
4
+ import type { CallableInputOf, CallableInputModeOf, CallableOutputOf, CallableResultOf, Domain, ResolvedFunction, ResolvedMethod, KernelSchema, schema } from '../../platform/schema/index.js';
5
5
  import type { LocalCallable } from '../action/index.js';
6
- export type DependencyCallOptions = Pick<SessionRequestOptions, 'timeoutMs' | 'idempotencyKey'>;
6
+ export type DependencyCallOptions = Pick<SessionRequestOptions, 'timeoutMs' | 'idempotencyKey' | 'body'>;
7
+ export type DependencyBinaryCallOptions = Pick<SessionRequestOptionsWithBody, 'timeoutMs' | 'idempotencyKey' | 'body'>;
7
8
  type InstanceMethodOf<D extends Domain> = Extract<LocalCallable<D>, ResolvedMethod<unknown, unknown, false>>;
8
9
  type FunctionOrStaticMethodOf<D extends Domain> = Exclude<LocalCallable<D>, ResolvedMethod<unknown, unknown, false>>;
10
+ type WithInputMode<C, Mode extends 'value' | 'binary'> = C extends unknown ? CallableInputModeOf<C> extends Mode ? C : never : never;
11
+ type ValueFunctionOrStaticMethodOf<D extends Domain> = WithInputMode<FunctionOrStaticMethodOf<D>, 'value'>;
12
+ type BinaryFunctionOf<D extends Domain> = WithInputMode<Extract<LocalCallable<D>, ResolvedFunction>, 'binary'>;
9
13
  type DependencyInputOf<C> = C extends unknown ? unknown extends CallableInputOf<C> ? Input : [CallableInputOf<C>] extends [void] ? undefined : CallableInputOf<C> : never;
10
14
  type DependencyResultOf<C> = unknown extends CallableOutputOf<C> ? unknown : CallableResultOf<C, Binary>;
11
15
  /** One declared dependency, bound to one explicitly selected invocation authority. */
12
16
  export interface DependencyInvoker<D extends Domain> {
13
17
  readonly invoke: {
14
- <const C extends FunctionOrStaticMethodOf<D>>(select: (domain: D) => C, input: NoInfer<DependencyInputOf<C>>, options?: DependencyCallOptions): Promise<DependencyResultOf<C>>;
18
+ <const C extends ValueFunctionOrStaticMethodOf<D>>(select: (domain: D) => C, input: NoInfer<DependencyInputOf<C>>, options?: DependencyCallOptions): Promise<DependencyResultOf<C>>;
15
19
  <const C extends InstanceMethodOf<D>>(select: (domain: D) => C, instance: NodeId, input: NoInfer<DependencyInputOf<C>>, options?: DependencyCallOptions): Promise<DependencyResultOf<C>>;
20
+ <const C extends BinaryFunctionOf<D>>(select: (domain: D) => C, metadata: NoInfer<DependencyInputOf<C>>, options: DependencyBinaryCallOptions): Promise<DependencyResultOf<C>>;
16
21
  };
17
22
  }
18
23
  /**
@@ -1,9 +1,11 @@
1
1
  import type { MutationPropsInput } from '@astrale-os/kernel-core/graph/mutate';
2
2
  import type { PropertyInputOf, ResolvedClass, ResolvedProperty } from '@astrale-os/kernel-dsl/v1';
3
+ import { PropertyValues } from '@astrale-os/kernel-dsl/v1/language';
4
+ import type { StoredPropertyInputOf } from '../../../platform/schema/property-input.js';
3
5
  export type RichPropertyInput<Class> = PropertyInputOf<Class>;
4
6
  export type RichPropertyName<Class> = Extract<keyof PropertyInputOf<Class>, string>;
5
7
  export interface RichPropertyConditions<Class> {
6
- readonly equals?: RichPropertyInput<Class>;
8
+ readonly equals?: StoredPropertyInputOf<Class>;
7
9
  readonly absent?: readonly RichPropertyName<Class>[];
8
10
  }
9
11
  export interface RichPropertyDelta<Class> {
@@ -12,11 +14,11 @@ export interface RichPropertyDelta<Class> {
12
14
  }
13
15
  export declare function properties<Class extends ResolvedClass>(selected: Class, input: RichPropertyInput<Class>): MutationPropsInput;
14
16
  export declare function conditions<Class extends ResolvedClass>(selected: Class, input: RichPropertyConditions<Class>): Readonly<{
15
- equals?: Readonly<Record<`${string}:class.${string}.property.${string}`, import("@astrale-os/kernel-dsl/value").Value>> | undefined;
17
+ equals?: PropertyValues | undefined;
16
18
  absent?: readonly `${string}:class.${string}.property.${string}`[] | undefined;
17
19
  }>;
18
20
  export declare function delta<Class extends ResolvedClass>(selected: Class, input: RichPropertyDelta<Class>): Readonly<{
19
- set: Readonly<Record<`${string}:class.${string}.property.${string}`, import("@astrale-os/kernel-dsl/value").Value>>;
21
+ set: Readonly<Record<`${string}:class.${string}.property.${string}`, import("@astrale-os/kernel-dsl/v1/language").PropertyValue>>;
20
22
  unset: readonly `${string}:class.${string}.property.${string}`[];
21
23
  }>;
22
24
  export declare function resolvedProperty<Class extends ResolvedClass>(selected: Class, name: RichPropertyName<Class>): ResolvedProperty;
@@ -1,10 +1,39 @@
1
1
  import { PropertyKey } from '@astrale-os/kernel-dsl/v1/addressing';
2
+ import { PropertyValues } from '@astrale-os/kernel-dsl/v1/language';
2
3
  export function properties(selected, input) {
3
- return selected.properties.from(input);
4
+ const ordinary = Object.create(null);
5
+ const receipts = [];
6
+ const sources = new Map();
7
+ for (const [name, value] of Object.entries(input)) {
8
+ const property = resolvedProperty(selected, name);
9
+ const previous = sources.get(property.key);
10
+ if (previous !== undefined) {
11
+ throw new TypeError(`Property ${property.key} is supplied through both ${JSON.stringify(previous)} and ${JSON.stringify(name)}.`);
12
+ }
13
+ sources.set(property.key, name);
14
+ if (isBlobProperty(property)) {
15
+ if (typeof value !== 'string') {
16
+ throw new TypeError(`Blob Property ${property.key} requires an upload receipt.`);
17
+ }
18
+ receipts.push([property.key, value]);
19
+ }
20
+ else {
21
+ ordinary[name] = value;
22
+ }
23
+ }
24
+ // Blob receipts are admitted by the Kernel executor. A property's ordinary validator expects
25
+ // the stored Ref, so run it only for ordinary write values before combining the two sets.
26
+ const admitted = selected.properties.from(ordinary);
27
+ return receipts.length === 0
28
+ ? admitted
29
+ : PropertyValues.create([
30
+ ...Object.entries(admitted).map(([key, value]) => [PropertyKey(key), value]),
31
+ ...receipts,
32
+ ]);
4
33
  }
5
34
  export function conditions(selected, input) {
6
35
  return Object.freeze({
7
- ...(input.equals === undefined ? {} : { equals: properties(selected, input.equals) }),
36
+ ...(input.equals === undefined ? {} : { equals: selected.properties.from(input.equals) }),
8
37
  ...(input.absent === undefined
9
38
  ? {}
10
39
  : {
@@ -12,6 +41,10 @@ export function conditions(selected, input) {
12
41
  }),
13
42
  });
14
43
  }
44
+ function isBlobProperty(property) {
45
+ const contract = property.definition.schema;
46
+ return (contract !== null && typeof contract === 'object' && contract['x-astrale-value-kind'] === 'blob');
47
+ }
15
48
  export function delta(selected, input) {
16
49
  return Object.freeze({
17
50
  set: properties(selected, input.set ?? {}),
@@ -49,7 +49,10 @@ export function authorStateTransition(core, input) {
49
49
  class: selectedClassKey,
50
50
  props: conditions(selectedClass, {
51
51
  ...(suppliedAbsent === undefined ? {} : { absent: suppliedAbsent }),
52
- equals: { ...suppliedEquals, ...from },
52
+ equals: {
53
+ ...suppliedEquals,
54
+ ...from,
55
+ },
53
56
  }),
54
57
  });
55
58
  core.updateNode({
@@ -1,2 +1,2 @@
1
- import type { ResolvedCallable } from '../../../application/action/index.js';
2
- export declare function input(callable: ResolvedCallable, value: unknown): void;
1
+ import type { ResolvedCallable, ActionBinaryInput } from '../../../application/action/index.js';
2
+ export declare function input(callable: ResolvedCallable, value: unknown, binary?: ActionBinaryInput): void;
@@ -1,4 +1,18 @@
1
- import { validateInput } from '../result/index.js';
2
- export function input(callable, value) {
1
+ import { isBinaryInput } from '@astrale-os/kernel-dsl/v1/language';
2
+ import { validateInput, protocolError } from '../result/index.js';
3
+ export function input(callable, value, binary) {
4
+ if (isBinaryInput(callable.input)) {
5
+ if (binary === undefined) {
6
+ throw protocolError('VALIDATION_ERROR', 'Binary Function request body is required.');
7
+ }
8
+ if (!Number.isSafeInteger(binary.length) ||
9
+ binary.length < 0 ||
10
+ binary.length > callable.input.maximumBytes) {
11
+ throw protocolError('LIMIT_EXCEEDED', 'Binary Function request body exceeds its limit.');
12
+ }
13
+ }
14
+ else if (binary !== undefined) {
15
+ throw protocolError('VALIDATION_ERROR', 'Callable does not accept a binary request body.');
16
+ }
3
17
  validateInput(callable, value);
4
18
  }
@@ -1,4 +1,5 @@
1
1
  import { Key } from '@astrale-os/kernel-dsl/v1/addressing';
2
+ import { isBinaryInput } from '@astrale-os/kernel-dsl/v1/language';
2
3
  import { reference } from '../../../platform/client/session/index.js';
3
4
  import { NodeId } from '../../../platform/graph/node/index.js';
4
5
  import { Path } from '../../../platform/graph/path/index.js';
@@ -20,19 +21,14 @@ function bindDependency(dependency, session) {
20
21
  const invoke = session.invoke.bind(session);
21
22
  return Object.freeze({
22
23
  async invoke(select, ...args) {
23
- if (typeof select !== 'function')
24
- invalid();
25
- const selected = select(dependency);
26
- if (selected === null ||
27
- typeof selected !== 'object' ||
28
- !('key' in selected) ||
29
- typeof selected.key !== 'string')
30
- invalid();
31
- const callable = dependency.callable(selected.key);
32
- if (callable !== selected || Key.origin(callable.key) !== dependency.origin)
33
- invalid();
34
- if ('owner' in callable && (!callable.executable || !ownMethod(dependency, callable)))
35
- invalid();
24
+ const callable = selectedCallable(dependency, select);
25
+ if (isBinaryInput(callable.input)) {
26
+ if ('owner' in callable || args.length !== 2)
27
+ invalid();
28
+ // Runtime selection is wider than the authored Function; mode was checked above.
29
+ const binaryReference = reference(dependency, callable);
30
+ return invoke(binaryReference, args[0], binaryOptions(args[1]));
31
+ }
36
32
  if ('owner' in callable && !callable.static) {
37
33
  if (args.length < 2 || args.length > 3 || typeof args[0] !== 'string')
38
34
  invalid();
@@ -45,6 +41,22 @@ function bindDependency(dependency, session) {
45
41
  },
46
42
  });
47
43
  }
44
+ function selectedCallable(domain, select) {
45
+ if (typeof select !== 'function')
46
+ invalid();
47
+ const selected = select(domain);
48
+ if (selected === null ||
49
+ typeof selected !== 'object' ||
50
+ !('key' in selected) ||
51
+ typeof selected.key !== 'string')
52
+ invalid();
53
+ const callable = domain.callable(selected.key);
54
+ if (callable !== selected || Key.origin(callable.key) !== domain.origin)
55
+ invalid();
56
+ if ('owner' in callable && (!callable.executable || !ownMethod(domain, callable)))
57
+ invalid();
58
+ return callable;
59
+ }
48
60
  function ownMethod(domain, callable) {
49
61
  const owner = Object.values(domain.classes).find((value) => value === callable.owner);
50
62
  if (owner === undefined)
@@ -63,6 +75,16 @@ function options(value) {
63
75
  // Canonical ClientSession admits option values and clamps them to the parent lifetime.
64
76
  return value;
65
77
  }
78
+ function binaryOptions(value) {
79
+ if (value === null ||
80
+ typeof value !== 'object' ||
81
+ Array.isArray(value) ||
82
+ !Object.hasOwn(value, 'body') ||
83
+ !(value.body instanceof Blob) ||
84
+ Reflect.ownKeys(value).some((key) => key !== 'body' && key !== 'timeoutMs'))
85
+ invalid();
86
+ return value;
87
+ }
66
88
  function invalid() {
67
89
  throw new TypeError('Dependency invocation requires one owned executable callable, its input, and valid instance/options.');
68
90
  }
@@ -1,5 +1,5 @@
1
1
  import type { HandlerRequest } from '@astrale-os/kernel-server';
2
- import type { ActionExecution, Caller, DomainGraphExecutors, DomainKernelSessions } from '../../../application/action/index.js';
2
+ import type { ActionExecution, ActionBinaryInput, Caller, DomainGraphExecutors, DomainKernelSessions } from '../../../application/action/index.js';
3
3
  import type { HandlerDependencies } from '../../../application/dependency/index.js';
4
4
  import type { MutationExecutor } from '../../../application/mutation/index.js';
5
5
  import type { QueryExecutor } from '../../../application/query/index.js';
@@ -10,6 +10,7 @@ import type { InvocationAuthentication } from '../admission/index.js';
10
10
  export interface DispatchContext {
11
11
  readonly domain: Domain;
12
12
  readonly input: unknown;
13
+ readonly body?: ActionBinaryInput;
13
14
  readonly caller: Caller;
14
15
  readonly dependencies: HandlerDependencies<Domain> | null;
15
16
  readonly kernel: DomainKernelSessions | null;
@@ -17,6 +17,7 @@ export function context(request, runtime, authentication, self) {
17
17
  return Object.freeze({
18
18
  domain: runtime.loaded.domain,
19
19
  input: request.call.input,
20
+ ...(request.context.binary === undefined ? {} : { body: request.context.binary }),
20
21
  caller: authentication.caller,
21
22
  kernel: authentication.graph === null
22
23
  ? null
@@ -26,11 +26,11 @@ export async function invoke(request, inputConfig) {
26
26
  const selected = select(inputConfig.runtime.loaded.registry, selectedTarget);
27
27
  if (selected === undefined)
28
28
  return Object.freeze({ kind: 'miss' });
29
- input(selected.implementation.callable, request.call.input);
29
+ input(selected.implementation.callable, request.call.input, request.context.binary);
30
30
  const admittedAuthentication = await authentication(selected.implementation.callable, request, release, inputConfig.runtime.loaded.domain, inputConfig.authority);
31
31
  const close = once(admittedAuthentication.close);
32
- active(request.context);
33
32
  try {
33
+ active(request.context);
34
34
  const dispatchContext = context(request, inputConfig.runtime, admittedAuthentication, selected.self);
35
35
  const output = selected.implementation.kind === 'action'
36
36
  ? await action(selected.implementation, dispatchContext)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/sdk",
3
- "version": "0.6.0-beta.7",
3
+ "version": "0.6.0-beta.9",
4
4
  "description": "Schema-first SDK for defining, composing, and deploying Astrale domains",
5
5
  "keywords": [
6
6
  "astrale",
@@ -237,11 +237,11 @@
237
237
  "registry": "https://registry.npmjs.org/"
238
238
  },
239
239
  "dependencies": {
240
- "@astrale-os/kernel-client": "0.6.0-beta.78",
241
- "@astrale-os/kernel-core": "0.9.0-beta.58",
242
- "@astrale-os/kernel-dsl": "0.2.0-beta.42",
243
- "@astrale-os/kernel-protocol": "0.5.0-beta.64",
244
- "@astrale-os/kernel-server": "0.5.0-beta.66",
240
+ "@astrale-os/kernel-client": "0.6.0-beta.82",
241
+ "@astrale-os/kernel-core": "0.9.0-beta.61",
242
+ "@astrale-os/kernel-dsl": "0.2.0-beta.43",
243
+ "@astrale-os/kernel-protocol": "0.5.0-beta.67",
244
+ "@astrale-os/kernel-server": "0.5.0-beta.69",
245
245
  "@lucide/icons": "1.34.0",
246
246
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
247
247
  "cross-spawn": "7.0.6",