mirror of
https://github.com/nestriness/nestri.git
synced 2026-08-01 17:34:15 +03:00
feat: Add Session
This commit is contained in:
@@ -44,6 +44,45 @@ export namespace Examples {
|
|||||||
|
|
||||||
export const Session = {
|
export const Session = {
|
||||||
id: Id("session"),
|
id: Id("session"),
|
||||||
|
description: "A gaming session for streaming gameplay",
|
||||||
|
retry: {
|
||||||
|
maxAttempts: 3,
|
||||||
|
factor: 2,
|
||||||
|
minTimeoutInMs: 1000,
|
||||||
|
maxTimeoutInMs: 30000,
|
||||||
|
randomize: true,
|
||||||
|
},
|
||||||
|
queue: {
|
||||||
|
name: "gaming-queue",
|
||||||
|
concurrencyLimit: 5,
|
||||||
|
},
|
||||||
|
machine: "ns3-large" as const,
|
||||||
|
maxDuration: 3600,
|
||||||
|
relayUrl: "https://relay.nestri.io",
|
||||||
|
resolution: "1920x1080" as const,
|
||||||
|
framerate: 60,
|
||||||
|
room: "game-room-123",
|
||||||
|
gpuVendor: "nvidia",
|
||||||
|
gpuName: "RTX 4090",
|
||||||
|
gpuIndex: 0,
|
||||||
|
gpuCardPath: "/dev/dri/renderD128",
|
||||||
|
videoCodec: "h264",
|
||||||
|
videoEncoder: "nvenc",
|
||||||
|
videoRateControl: "cbr" as const,
|
||||||
|
videoCqp: 26,
|
||||||
|
videoBitrate: 6000,
|
||||||
|
videoBitrateMax: 8000,
|
||||||
|
videoEncoderType: "hardware" as const,
|
||||||
|
videoBitDepth: 8 as const,
|
||||||
|
audioCaptureMethod: "pipewire" as const,
|
||||||
|
audioCodec: "opus",
|
||||||
|
audioEncoder: "opusenc",
|
||||||
|
audioRateControl: "cbr" as const,
|
||||||
|
audioBitrate: 128,
|
||||||
|
audioBitrateMax: 192,
|
||||||
|
zeroCopy: false,
|
||||||
|
verbose: false,
|
||||||
|
run: "async (payload, { ctx, signal }) => { /* session logic */ }",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const App = {
|
export const App = {
|
||||||
|
|||||||
@@ -1,3 +1,202 @@
|
|||||||
export * from "./json.js";
|
import { z } from "zod/v4";
|
||||||
export * from "./types.js";
|
import { Identifier } from "../id.js";
|
||||||
export * from "./schema.js";
|
import { Examples } from "../examples.js";
|
||||||
|
|
||||||
|
// Basic types we need
|
||||||
|
export const MachinePresetName = z.enum([
|
||||||
|
"ns3-micro",
|
||||||
|
"ns3-small",
|
||||||
|
"ns3-medium",
|
||||||
|
"ns3-large",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const ScreenResolutionPresetName = z.enum([
|
||||||
|
"1280x720",
|
||||||
|
"1080x1920",
|
||||||
|
"1440x2560",
|
||||||
|
"2160x3840",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const VideoCodecName = z.enum(["h264", "h265", "vp8", "vp9", "av1"]);
|
||||||
|
|
||||||
|
export const AudioCodecName = z.enum(["opus", "aac", "mp3", "pcm"]);
|
||||||
|
|
||||||
|
export const RetryOptions = z.object({
|
||||||
|
maxAttempts: z.number().int().optional(),
|
||||||
|
factor: z.number().optional(),
|
||||||
|
minTimeoutInMs: z.number().int().optional(),
|
||||||
|
maxTimeoutInMs: z.number().int().optional(),
|
||||||
|
randomize: z.boolean().optional(),
|
||||||
|
outOfMemory: z
|
||||||
|
.object({
|
||||||
|
machine: MachinePresetName.optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export namespace Session {
|
||||||
|
export const Info = z
|
||||||
|
.object({
|
||||||
|
id: Identifier.schema("session").meta({
|
||||||
|
description: "The unique identifier of the session",
|
||||||
|
example: Examples.Session.id,
|
||||||
|
}),
|
||||||
|
description: z.string().optional().meta({
|
||||||
|
description: "Description of what the session does",
|
||||||
|
example: Examples.Session.description,
|
||||||
|
}),
|
||||||
|
retry: RetryOptions.optional().meta({
|
||||||
|
description: "Retry configuration for failed session attempts",
|
||||||
|
example: Examples.Session.retry,
|
||||||
|
}),
|
||||||
|
queue: z
|
||||||
|
.object({
|
||||||
|
name: z.string().optional(),
|
||||||
|
concurrencyLimit: z.number().optional(),
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.meta({
|
||||||
|
description: "Queue configuration for concurrent session execution",
|
||||||
|
example: Examples.Session.queue,
|
||||||
|
}),
|
||||||
|
machine: MachinePresetName.optional().meta({
|
||||||
|
description: "Machine preset for the session",
|
||||||
|
example: Examples.Session.machine,
|
||||||
|
}),
|
||||||
|
maxDuration: z.number().optional().meta({
|
||||||
|
description: "Maximum duration in seconds the session can run",
|
||||||
|
example: Examples.Session.maxDuration,
|
||||||
|
}),
|
||||||
|
relayUrl: z.url().optional().meta({
|
||||||
|
description: "Nestri relay URL for streaming and signaling",
|
||||||
|
example: Examples.Session.relayUrl,
|
||||||
|
}),
|
||||||
|
resolution: ScreenResolutionPresetName.optional().meta({
|
||||||
|
description: "Display or stream resolution in 'WxH' format",
|
||||||
|
example: Examples.Session.resolution,
|
||||||
|
}),
|
||||||
|
framerate: z.number().optional().meta({
|
||||||
|
description: "Display or stream framerate",
|
||||||
|
example: Examples.Session.framerate,
|
||||||
|
}),
|
||||||
|
room: z.string().optional().meta({
|
||||||
|
description: "Room name or identifier for multiplayer sessions",
|
||||||
|
example: Examples.Session.room,
|
||||||
|
}),
|
||||||
|
gpuVendor: z.string().optional().meta({
|
||||||
|
description: "Preferred GPU vendor",
|
||||||
|
example: Examples.Session.gpuVendor,
|
||||||
|
}),
|
||||||
|
gpuName: z.string().optional().meta({
|
||||||
|
description: "Preferred GPU name",
|
||||||
|
example: Examples.Session.gpuName,
|
||||||
|
}),
|
||||||
|
gpuIndex: z.number().optional().meta({
|
||||||
|
description: "Specific GPU index to use",
|
||||||
|
example: Examples.Session.gpuIndex,
|
||||||
|
}),
|
||||||
|
gpuCardPath: z.string().optional().meta({
|
||||||
|
description: "Force specific GPU card by /dev/dri path",
|
||||||
|
example: Examples.Session.gpuCardPath,
|
||||||
|
}),
|
||||||
|
videoCodec: VideoCodecName.optional().meta({
|
||||||
|
description: "Preferred video codec for streaming",
|
||||||
|
example: Examples.Session.videoCodec,
|
||||||
|
}),
|
||||||
|
videoEncoder: z.string().optional().meta({
|
||||||
|
description: "Override video encoder implementation",
|
||||||
|
example: Examples.Session.videoEncoder,
|
||||||
|
}),
|
||||||
|
videoRateControl: z
|
||||||
|
.enum(["cbr", "vbr", "cq", "lossless"])
|
||||||
|
.optional()
|
||||||
|
.meta({
|
||||||
|
description: "Video rate control method",
|
||||||
|
example: Examples.Session.videoRateControl,
|
||||||
|
}),
|
||||||
|
videoCqp: z.number().min(1).max(51).optional().meta({
|
||||||
|
description: "Constant Quantization Parameter for quality control",
|
||||||
|
example: Examples.Session.videoCqp,
|
||||||
|
}),
|
||||||
|
videoBitrate: z.number().min(1).optional().meta({
|
||||||
|
description: "Target video bitrate in kbps",
|
||||||
|
example: Examples.Session.videoBitrate,
|
||||||
|
}),
|
||||||
|
videoBitrateMax: z.number().min(1).optional().meta({
|
||||||
|
description: "Maximum video bitrate in kbps",
|
||||||
|
example: Examples.Session.videoBitrateMax,
|
||||||
|
}),
|
||||||
|
videoEncoderType: z.enum(["hardware", "software"]).optional().meta({
|
||||||
|
description: "Encoder type: hardware or software",
|
||||||
|
example: Examples.Session.videoEncoderType,
|
||||||
|
}),
|
||||||
|
videoBitDepth: z
|
||||||
|
.union([z.literal(8), z.literal(10)])
|
||||||
|
.optional()
|
||||||
|
.meta({
|
||||||
|
description: "Video bit depth (8 or 10)",
|
||||||
|
example: Examples.Session.videoBitDepth,
|
||||||
|
}),
|
||||||
|
audioCaptureMethod: z
|
||||||
|
.enum(["pipewire", "pulse", "alsa"])
|
||||||
|
.optional()
|
||||||
|
.meta({
|
||||||
|
description: "Audio capture method",
|
||||||
|
example: Examples.Session.audioCaptureMethod,
|
||||||
|
}),
|
||||||
|
audioCodec: AudioCodecName.optional().meta({
|
||||||
|
description: "Preferred audio codec for streaming",
|
||||||
|
example: Examples.Session.audioCodec,
|
||||||
|
}),
|
||||||
|
audioEncoder: z.string().optional().meta({
|
||||||
|
description: "Override audio encoder binary",
|
||||||
|
example: Examples.Session.audioEncoder,
|
||||||
|
}),
|
||||||
|
audioRateControl: z.enum(["cbr", "vbr"]).optional().meta({
|
||||||
|
description: "Audio rate control method",
|
||||||
|
example: Examples.Session.audioRateControl,
|
||||||
|
}),
|
||||||
|
audioBitrate: z.number().min(1).optional().meta({
|
||||||
|
description: "Target audio bitrate in kbps",
|
||||||
|
example: Examples.Session.audioBitrate,
|
||||||
|
}),
|
||||||
|
audioBitrateMax: z.number().optional().meta({
|
||||||
|
description: "Maximum audio bitrate in kbps",
|
||||||
|
example: Examples.Session.audioBitrateMax,
|
||||||
|
}),
|
||||||
|
zeroCopy: z.boolean().optional().meta({
|
||||||
|
description: "Whether to use DMA-BUF for zero-copy video pipelines",
|
||||||
|
example: Examples.Session.zeroCopy,
|
||||||
|
}),
|
||||||
|
verbose: z.boolean().optional().meta({
|
||||||
|
description: "Enable verbose output for debugging",
|
||||||
|
example: Examples.Session.verbose,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.meta({
|
||||||
|
ref: "Session",
|
||||||
|
description:
|
||||||
|
"Represents a session configuration with streaming and execution options",
|
||||||
|
example: Examples.Session,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Info = z.infer<typeof Info>;
|
||||||
|
|
||||||
|
// Session interface that matches the SDK session object
|
||||||
|
export interface Session {
|
||||||
|
/** Optional description of the session */
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/** Start a session without waiting for the result */
|
||||||
|
start: (options?: any, requestOptions?: any) => Promise<any>;
|
||||||
|
|
||||||
|
/** Start a session and wait for the result */
|
||||||
|
startAndWait: (options?: any) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session configuration type that includes the run function
|
||||||
|
export type Config = Omit<Info, "id"> & {
|
||||||
|
/** The function that executes when the session is started */
|
||||||
|
run: (ctx: any) => Promise<any>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
import { z } from "zod/v4";
|
|
||||||
|
|
||||||
const LiteralSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
||||||
type Literal = z.infer<typeof LiteralSchema>;
|
|
||||||
|
|
||||||
export type DeserializedJson =
|
|
||||||
| Literal
|
|
||||||
| { [key: string]: DeserializedJson }
|
|
||||||
| DeserializedJson[];
|
|
||||||
|
|
||||||
export const DeserializedJsonSchema: z.ZodType<DeserializedJson> = z.lazy(() =>
|
|
||||||
z.union([
|
|
||||||
LiteralSchema,
|
|
||||||
z.array(DeserializedJsonSchema),
|
|
||||||
z.record(z.string(), DeserializedJsonSchema),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const SerializableSchema = z.union([
|
|
||||||
z.string(),
|
|
||||||
z.number(),
|
|
||||||
z.boolean(),
|
|
||||||
z.null(),
|
|
||||||
z.date(),
|
|
||||||
z.undefined(),
|
|
||||||
z.symbol(),
|
|
||||||
]);
|
|
||||||
type Serializable = z.infer<typeof SerializableSchema>;
|
|
||||||
|
|
||||||
export type SerializableJson =
|
|
||||||
| Serializable
|
|
||||||
| { [key: string]: SerializableJson }
|
|
||||||
| SerializableJson[];
|
|
||||||
|
|
||||||
export const SerializableJsonSchema: z.ZodType<SerializableJson> = z.lazy(() =>
|
|
||||||
z.union([
|
|
||||||
SerializableSchema,
|
|
||||||
z.array(SerializableJsonSchema),
|
|
||||||
z.record(z.string(), SerializableJsonSchema),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON Schema type definition - compatible with JSON Schema Draft 7
|
|
||||||
* Based on the JSONSchema7 type from @types/json-schema but defined inline to avoid import issues
|
|
||||||
*/
|
|
||||||
export interface JSONSchema {
|
|
||||||
$id?: string;
|
|
||||||
$ref?: string;
|
|
||||||
$schema?: string;
|
|
||||||
$comment?: string;
|
|
||||||
|
|
||||||
type?: JSONSchemaType | JSONSchemaType[];
|
|
||||||
enum?: any[];
|
|
||||||
const?: any;
|
|
||||||
|
|
||||||
// Number/Integer validations
|
|
||||||
multipleOf?: number;
|
|
||||||
maximum?: number;
|
|
||||||
exclusiveMaximum?: number;
|
|
||||||
minimum?: number;
|
|
||||||
exclusiveMinimum?: number;
|
|
||||||
|
|
||||||
// String validations
|
|
||||||
maxLength?: number;
|
|
||||||
minLength?: number;
|
|
||||||
pattern?: string;
|
|
||||||
format?: string;
|
|
||||||
|
|
||||||
// Array validations
|
|
||||||
items?: JSONSchema | JSONSchema[];
|
|
||||||
additionalItems?: JSONSchema | boolean;
|
|
||||||
maxItems?: number;
|
|
||||||
minItems?: number;
|
|
||||||
uniqueItems?: boolean;
|
|
||||||
contains?: JSONSchema;
|
|
||||||
|
|
||||||
// Object validations
|
|
||||||
maxProperties?: number;
|
|
||||||
minProperties?: number;
|
|
||||||
required?: string[];
|
|
||||||
properties?: Record<string, JSONSchema>;
|
|
||||||
patternProperties?: Record<string, JSONSchema>;
|
|
||||||
additionalProperties?: JSONSchema | boolean;
|
|
||||||
dependencies?: Record<string, JSONSchema | string[]>;
|
|
||||||
propertyNames?: JSONSchema;
|
|
||||||
|
|
||||||
// Conditional schemas
|
|
||||||
if?: JSONSchema;
|
|
||||||
then?: JSONSchema;
|
|
||||||
else?: JSONSchema;
|
|
||||||
|
|
||||||
// Boolean logic
|
|
||||||
allOf?: JSONSchema[];
|
|
||||||
anyOf?: JSONSchema[];
|
|
||||||
oneOf?: JSONSchema[];
|
|
||||||
not?: JSONSchema;
|
|
||||||
|
|
||||||
// Metadata
|
|
||||||
title?: string;
|
|
||||||
description?: string;
|
|
||||||
default?: any;
|
|
||||||
readOnly?: boolean;
|
|
||||||
writeOnly?: boolean;
|
|
||||||
examples?: any[];
|
|
||||||
|
|
||||||
// Additional properties for extensibility
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type JSONSchemaType =
|
|
||||||
| "string"
|
|
||||||
| "number"
|
|
||||||
| "integer"
|
|
||||||
| "boolean"
|
|
||||||
| "object"
|
|
||||||
| "array"
|
|
||||||
| "null";
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
export type SchemaZodEsque<TInput, TParsedInput> = {
|
|
||||||
_input: TInput;
|
|
||||||
_output: TParsedInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function isSchemaZodEsque<TInput, TParsedInput>(
|
|
||||||
schema: Schema,
|
|
||||||
): schema is SchemaZodEsque<TInput, TParsedInput> {
|
|
||||||
return (
|
|
||||||
typeof schema === "object" &&
|
|
||||||
"_def" in schema &&
|
|
||||||
"parse" in schema &&
|
|
||||||
"parseAsync" in schema &&
|
|
||||||
"safeParse" in schema
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SchemaValibotEsque<TInput, TParsedInput> = {
|
|
||||||
schema: {
|
|
||||||
_types?: {
|
|
||||||
input: TInput;
|
|
||||||
output: TParsedInput;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function isSchemaValibotEsque<TInput, TParsedInput>(
|
|
||||||
schema: Schema,
|
|
||||||
): schema is SchemaValibotEsque<TInput, TParsedInput> {
|
|
||||||
return typeof schema === "object" && "_types" in schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SchemaArkTypeEsque<TInput, TParsedInput> = {
|
|
||||||
inferIn: TInput;
|
|
||||||
infer: TParsedInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function isSchemaArkTypeEsque<TInput, TParsedInput>(
|
|
||||||
schema: Schema,
|
|
||||||
): schema is SchemaArkTypeEsque<TInput, TParsedInput> {
|
|
||||||
return (
|
|
||||||
typeof schema === "object" && "_inferIn" in schema && "_infer" in schema
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SchemaMyZodEsque<TInput> = {
|
|
||||||
parse: (input: any) => TInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SchemaSuperstructEsque<TInput> = {
|
|
||||||
create: (input: unknown) => TInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SchemaCustomValidatorEsque<TInput> = (
|
|
||||||
input: unknown,
|
|
||||||
) => Promise<TInput> | TInput;
|
|
||||||
|
|
||||||
export type SchemaYupEsque<TInput> = {
|
|
||||||
validateSync: (input: unknown) => TInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SchemaScaleEsque<TInput> = {
|
|
||||||
assert(value: unknown): asserts value is TInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SchemaWithoutInput<TInput> =
|
|
||||||
| SchemaCustomValidatorEsque<TInput>
|
|
||||||
| SchemaMyZodEsque<TInput>
|
|
||||||
| SchemaScaleEsque<TInput>
|
|
||||||
| SchemaSuperstructEsque<TInput>
|
|
||||||
| SchemaYupEsque<TInput>;
|
|
||||||
|
|
||||||
export type SchemaWithInputOutput<TInput, TParsedInput> =
|
|
||||||
| SchemaZodEsque<TInput, TParsedInput>
|
|
||||||
| SchemaValibotEsque<TInput, TParsedInput>
|
|
||||||
| SchemaArkTypeEsque<TInput, TParsedInput>;
|
|
||||||
|
|
||||||
export type Schema = SchemaWithInputOutput<any, any> | SchemaWithoutInput<any>;
|
|
||||||
|
|
||||||
export type inferSchema<TSchema extends Schema> =
|
|
||||||
TSchema extends SchemaWithInputOutput<infer $TIn, infer $TOut>
|
|
||||||
? {
|
|
||||||
in: $TIn;
|
|
||||||
out: $TOut;
|
|
||||||
}
|
|
||||||
: TSchema extends SchemaWithoutInput<infer $InOut>
|
|
||||||
? {
|
|
||||||
in: $InOut;
|
|
||||||
out: $InOut;
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export type inferSchemaIn<
|
|
||||||
TSchema extends Schema | undefined,
|
|
||||||
TDefault = unknown,
|
|
||||||
> = TSchema extends Schema ? inferSchema<TSchema>["in"] : TDefault;
|
|
||||||
|
|
||||||
export type inferSchemaOut<
|
|
||||||
TSchema extends Schema | undefined,
|
|
||||||
TDefault = unknown,
|
|
||||||
> = TSchema extends Schema ? inferSchema<TSchema>["out"] : TDefault;
|
|
||||||
|
|
||||||
export type SchemaParseFn<TType> = (value: unknown) => Promise<TType> | TType;
|
|
||||||
export type AnySchemaParseFn = SchemaParseFn<any>;
|
|
||||||
|
|
||||||
export function getSchemaParseFn<TType>(
|
|
||||||
procedureParser: Schema,
|
|
||||||
): SchemaParseFn<TType> {
|
|
||||||
const parser = procedureParser as any;
|
|
||||||
|
|
||||||
if (typeof parser === "function" && typeof parser.assert === "function") {
|
|
||||||
// ParserArkTypeEsque - arktype schemas shouldn't be called as a function because they return a union type instead of throwing
|
|
||||||
return parser.assert.bind(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof parser === "function") {
|
|
||||||
// ParserValibotEsque (>= v0.31.0)
|
|
||||||
// ParserCustomValidatorEsque
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof parser.parseAsync === "function") {
|
|
||||||
// ParserZodEsque
|
|
||||||
return parser.parseAsync.bind(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof parser.parse === "function") {
|
|
||||||
// ParserZodEsque
|
|
||||||
// ParserValibotEsque (< v0.13.0)
|
|
||||||
return parser.parse.bind(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof parser.validateSync === "function") {
|
|
||||||
// ParserYupEsque
|
|
||||||
return parser.validateSync.bind(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof parser.create === "function") {
|
|
||||||
// ParserSuperstructEsque
|
|
||||||
return parser.create.bind(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof parser.assert === "function") {
|
|
||||||
// ParserScaleEsque
|
|
||||||
return (value) => {
|
|
||||||
parser.assert(value);
|
|
||||||
return value as TType;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Could not find a validator fn");
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,217 +1,45 @@
|
|||||||
import {
|
import { Identifier } from "@nestri/core/id";
|
||||||
type Session,
|
import { Session } from "@nestri/core/session/index";
|
||||||
type RunTypes,
|
|
||||||
type InitOutput,
|
|
||||||
type StartOptions,
|
|
||||||
type inferSchemaIn,
|
|
||||||
type SchemaParseFn,
|
|
||||||
type SessionSchema,
|
|
||||||
type SessionOptions,
|
|
||||||
type SessionWithSchema,
|
|
||||||
type SessionWithSchemaOptions,
|
|
||||||
type RunHandleFromTypes,
|
|
||||||
type AnyRunTypes,
|
|
||||||
type SessionOptionsWithSchema,
|
|
||||||
getSchemaParseFn,
|
|
||||||
SessionRunPromise,
|
|
||||||
} from "@nestri/core/session/index";
|
|
||||||
|
|
||||||
// Overload: when payloadSchema is provided, payload type should be any
|
// Simple session creation function using our clean Session types
|
||||||
export function createRun<
|
export function createSession(config: Session.Config): Session.Session {
|
||||||
TIdentifier extends string,
|
const session: Session.Session = {
|
||||||
TOutput = unknown,
|
description: config.description,
|
||||||
TInitOutput extends InitOutput = any,
|
start: async (payload: any) => {
|
||||||
>(
|
console.log(
|
||||||
params: SessionOptionsWithSchema<TIdentifier, TOutput, TInitOutput>,
|
`Starting session ${config.description} with payload:`,
|
||||||
): Session<TIdentifier, any, TOutput>;
|
|
||||||
|
|
||||||
// Overload: normal case without payloadSchema
|
|
||||||
export function createRun<
|
|
||||||
TIdentifier extends string,
|
|
||||||
TInput = void,
|
|
||||||
TOutput = unknown,
|
|
||||||
TInitOutput extends InitOutput = any,
|
|
||||||
>(
|
|
||||||
params: SessionOptions<TIdentifier, TInput, TOutput, TInitOutput>,
|
|
||||||
): Session<TIdentifier, TInput, TOutput>;
|
|
||||||
|
|
||||||
export function createRun<
|
|
||||||
TIdentifier extends string,
|
|
||||||
TInput = void,
|
|
||||||
TOutput = unknown,
|
|
||||||
TInitOutput extends InitOutput = any,
|
|
||||||
>(
|
|
||||||
params:
|
|
||||||
| SessionOptions<TIdentifier, TInput, TOutput, TInitOutput>
|
|
||||||
| SessionOptionsWithSchema<TIdentifier, TOutput, TInitOutput>,
|
|
||||||
): Session<TIdentifier, TInput, TOutput> | Session<TIdentifier, any, TOutput> {
|
|
||||||
const session: Session<TIdentifier, TInput, TOutput> = {
|
|
||||||
id: params.id,
|
|
||||||
description: params.description,
|
|
||||||
jsonSchema: params.jsonSchema,
|
|
||||||
start: async (payload, options) => {
|
|
||||||
return await run_internal<RunTypes<TIdentifier, TInput, TOutput>>(
|
|
||||||
"run()",
|
|
||||||
params.id,
|
|
||||||
payload,
|
payload,
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
queue: params.queue?.name,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
// TODO: Implement actual session start logic
|
||||||
|
return {
|
||||||
|
id: `run_${Date.now()}` as `run_${string}`,
|
||||||
|
sessionIdentifier: config.description,
|
||||||
|
publicAccessToken: "mock_token",
|
||||||
|
};
|
||||||
},
|
},
|
||||||
startAndWait: (payload, options) => {
|
|
||||||
return new SessionRunPromise<TIdentifier, TOutput>((resolve, reject) => {
|
startAndWait: (payload: any) => {
|
||||||
triggerAndWait_internal<TIdentifier, TInput, TOutput>(
|
console.log(
|
||||||
"triggerAndWait()",
|
`Starting session ${config.description} and waiting with payload:`,
|
||||||
params.id,
|
payload,
|
||||||
payload,
|
);
|
||||||
undefined,
|
// TODO: Implement actual session start and wait logic
|
||||||
{
|
return new Promise((resolve) => {
|
||||||
queue: params.queue?.name,
|
setTimeout(() => {
|
||||||
...options,
|
resolve({
|
||||||
},
|
ok: true,
|
||||||
)
|
id: `run_${Date.now()}` as any,
|
||||||
.then((result) => {
|
sessionIdentifier: config.description,
|
||||||
resolve(result);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
});
|
||||||
}, params.id);
|
}, 1000);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
registerTaskLifecycleHooks(params.id, params);
|
// Store the run function for later execution
|
||||||
|
(session as any)._runFunction = config.run;
|
||||||
resourceCatalog.registerTaskMetadata({
|
|
||||||
id: params.id,
|
|
||||||
description: params.description,
|
|
||||||
queue: params.queue,
|
|
||||||
retry: params.retry
|
|
||||||
? { ...defaultRetryOptions, ...params.retry }
|
|
||||||
: undefined,
|
|
||||||
machine:
|
|
||||||
typeof params.machine === "string"
|
|
||||||
? { preset: params.machine }
|
|
||||||
: params.machine,
|
|
||||||
maxDuration: params.maxDuration,
|
|
||||||
payloadSchema: params.jsonSchema,
|
|
||||||
fns: {
|
|
||||||
run: params.run,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const queue = params.queue;
|
|
||||||
|
|
||||||
if (queue && typeof queue.name === "string") {
|
|
||||||
resourceCatalog.registerQueueMetadata({
|
|
||||||
name: queue.name,
|
|
||||||
concurrencyLimit: queue.concurrencyLimit,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
session[Symbol.for("nestri/session")] = true;
|
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSchemaTask<
|
export const session = createSession;
|
||||||
TIdentifier extends string,
|
|
||||||
TSchema extends SessionSchema | undefined = undefined,
|
|
||||||
TOutput = unknown,
|
|
||||||
TInitOutput extends InitOutput = any,
|
|
||||||
>(
|
|
||||||
params: SessionWithSchemaOptions<TIdentifier, TSchema, TOutput, TInitOutput>,
|
|
||||||
): SessionWithSchema<TIdentifier, TSchema, TOutput> {
|
|
||||||
const parsePayload = params.schema
|
|
||||||
? getSchemaParseFn<inferSchemaIn<TSchema>>(params.schema)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const session: SessionWithSchema<TIdentifier, TSchema, TOutput> = {
|
|
||||||
id: params.id,
|
|
||||||
description: params.description,
|
|
||||||
schema: params.schema,
|
|
||||||
start: async (payload, options, requestOptions) => {
|
|
||||||
return await run_internal<
|
|
||||||
RunTypes<TIdentifier, inferSchemaIn<TSchema>, TOutput>
|
|
||||||
>(
|
|
||||||
"run()",
|
|
||||||
params.id,
|
|
||||||
payload,
|
|
||||||
parsePayload,
|
|
||||||
{
|
|
||||||
queue: params.queue?.name,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
requestOptions,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
startAndWait: (payload, options) => {
|
|
||||||
return new SessionRunPromise<TIdentifier, TOutput>((resolve, reject) => {
|
|
||||||
triggerAndWait_internal<TIdentifier, inferSchemaIn<TSchema>, TOutput>(
|
|
||||||
"triggerAndWait()",
|
|
||||||
params.id,
|
|
||||||
payload,
|
|
||||||
parsePayload,
|
|
||||||
{
|
|
||||||
queue: params.queue?.name,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.then((result) => {
|
|
||||||
resolve(result);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
}, params.id);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
registerTaskLifecycleHooks(params.id, params);
|
|
||||||
|
|
||||||
resourceCatalog.registerTaskMetadata({
|
|
||||||
id: params.id,
|
|
||||||
description: params.description,
|
|
||||||
queue: params.queue,
|
|
||||||
retry: params.retry
|
|
||||||
? { ...defaultRetryOptions, ...params.retry }
|
|
||||||
: undefined,
|
|
||||||
machine: { preset: params.machine },
|
|
||||||
maxDuration: params.maxDuration,
|
|
||||||
fns: {
|
|
||||||
run: params.run,
|
|
||||||
parsePayload,
|
|
||||||
},
|
|
||||||
schema: params.schema,
|
|
||||||
});
|
|
||||||
|
|
||||||
const queue = params.queue;
|
|
||||||
|
|
||||||
if (queue && typeof queue.name === "string") {
|
|
||||||
resourceCatalog.registerQueueMetadata({
|
|
||||||
name: queue.name,
|
|
||||||
concurrencyLimit: queue.concurrencyLimit,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-expect-error
|
|
||||||
session[Symbol.for("nestri/session")] = true;
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const session = createRun;
|
|
||||||
export const schemaSession = createSchemaTask;
|
|
||||||
|
|
||||||
async function run_internal<TRunTypes extends AnyRunTypes>(
|
|
||||||
name: string,
|
|
||||||
id: TRunTypes["sessionIdentifier"],
|
|
||||||
payload: TRunTypes["payload"],
|
|
||||||
parsePayload?: SchemaParseFn<TRunTypes["payload"]>,
|
|
||||||
options?: StartOptions,
|
|
||||||
requestOptions?: any, //TriggerApiRequestOptions,
|
|
||||||
): Promise<RunHandleFromTypes<TRunTypes>> {}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user