diff --git a/MIGRATION.md b/MIGRATION.md index d893573..cc1bfeb 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -16,8 +16,8 @@ This major change refactors the client and types for all Howso Engine operations ### Client Changes -- `BaseClient` has been renamed to `AbstractHowsoClient`. -- A new intermediary autogenerated abstract client `TraineeClient` has been introduced. +- `BaseClient` has been renamed to `AbstractBaseClient`. +- A new intermediary autogenerated abstract client `AbstractTraineeClient` has been introduced. - `WasmClient` has been renamed to `HowsoWorkerClient`. - `ProblemError` has been renamed to `HowsoError`. - `ValidationError` has been renamed to `HowsoValidationError`. diff --git a/README.md b/README.md index 08ac585..4f8cf5c 100644 --- a/README.md +++ b/README.md @@ -79,11 +79,12 @@ You can then create the worker client using a url import: ```ts import howsoUrl from "@howso/engine/lib/howso.caml?url"; import migrationsUrl from "@howso/engine/lib/migrations.caml?url"; -import { type ClientOptions, HowsoWorkerClient } from "@howso/engine"; +import { type ClientOptions, HowsoWorkerClient, BrowserFileSystem } from "@howso/engine"; const getClient = async (options?: ClientOptions): Promise => { const worker = new Worker(new URL("@/workers/AmalgamWorker", import.meta.url), { type: "module" }); - const client = new HowsoWorkerClient(worker, { + const fs = new BrowserFileSystem(worker); + const client = new HowsoWorkerClient(worker, fs, { howsoUrl, migrationsUrl, ...options, diff --git a/codegen/filters/index.ts b/codegen/filters/index.ts index 11da274..fedd012 100644 --- a/codegen/filters/index.ts +++ b/codegen/filters/index.ts @@ -13,6 +13,7 @@ export function registerFilters(env: Environment) { env.addFilter(strings.pascalCase.name, strings.pascalCase); env.addFilter(strings.camelCase.name, strings.camelCase); env.addFilter(strings.toJson.name, strings.toJson); + env.addFilter(strings.autoQuote.name, strings.autoQuote); env.addFilter(types.isString.name, types.isString); env.addFilter(types.isArray.name, types.isArray); env.addFilter(types.convertType.name, types.convertType); diff --git a/codegen/filters/strings.ts b/codegen/filters/strings.ts index 79ef06f..3dba812 100644 --- a/codegen/filters/strings.ts +++ b/codegen/filters/strings.ts @@ -13,3 +13,12 @@ export function camelCase(value: any) { export function toJson(value: any) { return JSON.stringify(value); } + +export function autoQuote(value: any) { + // Quote values for safe use as object keys + const str = typeof value === "string" ? value : String(value); + if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str)) { + return str; + } + return this.env.filters.safe(JSON.stringify(str)); +} diff --git a/codegen/generator.ts b/codegen/generator.ts index 19f9571..9743b06 100644 --- a/codegen/generator.ts +++ b/codegen/generator.ts @@ -76,7 +76,7 @@ export class Generator { targetLabels[label] = definition; } } - this.renderFile(this.clientDir, "trainee.ts", "client/trainee.njk", { + this.renderFile(this.clientDir, "AbstractTraineeClient.ts", "client/AbstractTraineeClient.njk", { labels: targetLabels, shims: this.responseShims, }); diff --git a/codegen/templates/client/trainee.njk b/codegen/templates/client/AbstractTraineeClient.njk similarity index 95% rename from codegen/templates/client/trainee.njk rename to codegen/templates/client/AbstractTraineeClient.njk index 367c316..3e9dfb4 100644 --- a/codegen/templates/client/trainee.njk +++ b/codegen/templates/client/AbstractTraineeClient.njk @@ -5,9 +5,9 @@ import type { ClientResponse, Session, Trainee } from "../types"; import type * as schemas from "../types/schemas"; import type * as shims from "../types/shims"; -import { AbstractHowsoClient } from "./base"; +import { AbstractBaseClient } from "./AbstractBaseClient"; -export abstract class TraineeClient extends AbstractHowsoClient { +export abstract class AbstractTraineeClient extends AbstractBaseClient { /** Create a new Trainee. */ public abstract createTrainee(trainee: Omit): Promise; diff --git a/codegen/templates/schemas/label.njk b/codegen/templates/schemas/label.njk index bdbab41..7f40cbe 100644 --- a/codegen/templates/schemas/label.njk +++ b/codegen/templates/schemas/label.njk @@ -4,7 +4,7 @@ export type {{ name }}Request = { {%- for key, schema in parameters | dictsort -%} {{ comment(schema) | indent(2) }} - {{ key }}{% if schema.required != true %}?{% endif %}: {{ field(schema) }}; + {{ key | autoQuote }}{% if schema.required != true %}?{% endif %}: {{ field(schema) }}; {%- if not loop.last %} {% endif %} {%- endfor %} diff --git a/codegen/templates/schemas/types/_assoc.njk b/codegen/templates/schemas/types/_assoc.njk index 4c631c7..6765408 100644 --- a/codegen/templates/schemas/types/_assoc.njk +++ b/codegen/templates/schemas/types/_assoc.njk @@ -3,12 +3,17 @@ { {%- for key, value in schema.indices | dictsort -%} {{ comment(value) | indent(2) }} - {{ key }}{% if not value | isSchemaOrRef or value.required != true %}?{% endif %}: {{ field(value) | indent(2) }}; + {{ key | autoQuote }}{% if not value | isSchemaOrRef or value.required != true %}?{% endif %}: {{ field(value) | indent(2) }}; {%- endfor %} } {%- if schema.additional_indices %} & Record {%- endif %} + {%- if schema.dynamic_indices %} + {%- for dynamic_key, dynamic_value in schema.dynamic_indices | dictsort %} + & Record<`{{ dynamic_key }}`, {{ field(dynamic_value) }}> + {%- endfor %} + {%- endif %} {%- elif schema.additional_indices -%} Record {%- else -%} diff --git a/src/client/base.ts b/src/client/AbstractBaseClient.ts similarity index 98% rename from src/client/base.ts rename to src/client/AbstractBaseClient.ts index f8333be..055722a 100644 --- a/src/client/base.ts +++ b/src/client/AbstractBaseClient.ts @@ -13,7 +13,7 @@ export type ExecuteResponse = { warnings: string[]; }; -export abstract class AbstractHowsoClient { +export abstract class AbstractBaseClient { protected abstract cache: CacheMap; /** diff --git a/src/client/trainee.ts b/src/client/AbstractTraineeClient.ts similarity index 99% rename from src/client/trainee.ts rename to src/client/AbstractTraineeClient.ts index 295a39c..1869ae4 100644 --- a/src/client/trainee.ts +++ b/src/client/AbstractTraineeClient.ts @@ -5,9 +5,9 @@ import type { ClientResponse, Session, Trainee } from "../types"; import type * as schemas from "../types/schemas"; import type * as shims from "../types/shims"; -import { AbstractHowsoClient } from "./base"; +import { AbstractBaseClient } from "./AbstractBaseClient"; -export abstract class TraineeClient extends AbstractHowsoClient { +export abstract class AbstractTraineeClient extends AbstractBaseClient { /** Create a new Trainee. */ public abstract createTrainee(trainee: Omit): Promise; diff --git a/src/client/worker/HowsoWorkerClient.node.test.ts b/src/client/worker/HowsoWorkerClient.node.test.ts new file mode 100644 index 0000000..5a1ff11 --- /dev/null +++ b/src/client/worker/HowsoWorkerClient.node.test.ts @@ -0,0 +1,38 @@ +/** + * @jest-environment node + */ + +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { Worker } from "node:worker_threads"; +import { inferFeatureAttributes } from "../../features"; +import { HowsoWorkerClient } from "./HowsoWorkerClient"; +import { NodeFileSystem } from "./filesystem"; + +global.structuredClone = (val) => JSON.parse(JSON.stringify(val)); +describe("Node HowsoWorkerClient", () => { + let worker: Worker; + let client: HowsoWorkerClient; + beforeAll(async () => { + worker = new Worker(resolve(__dirname, "../../tests/assets/NodeWorker.js")); + const fs = new NodeFileSystem(worker); + client = new HowsoWorkerClient(worker, fs, { + trace: false, + howsoUrl: resolve(__dirname, "../../engine/howso.caml"), + }); + await client.setup(); + const dataPath = resolve(__dirname, "../../tests/assets/iris.json"); + const data = JSON.parse(readFileSync(dataPath, { encoding: "utf8" })); + const feature_attributes = await inferFeatureAttributes(data, "array", {}); + const trainee = await client.createTrainee({ name: "My Trainee" }); + await client.setFeatureAttributes(trainee.id, { feature_attributes }); + }); + afterAll(() => { + worker?.terminate(); + }); + it("should queryTrainees", async () => { + const trainees = await client.queryTrainees(); + expect(trainees.length).toBe(1); + expect(trainees[0].name).toBe("My Trainee"); + }); +}); diff --git a/src/client/worker/client.ts b/src/client/worker/HowsoWorkerClient.ts similarity index 96% rename from src/client/worker/client.ts rename to src/client/worker/HowsoWorkerClient.ts index f80bf62..d99fe71 100644 --- a/src/client/worker/client.ts +++ b/src/client/worker/HowsoWorkerClient.ts @@ -5,14 +5,15 @@ import { type AmalgamRequest, type AmalgamResponseBody, } from "@howso/amalgam-lang"; +import type { Worker as NodeWorker } from "node:worker_threads"; import { v4 as uuid } from "uuid"; import type { FeatureAttributesIndex, Session, Trainee, TrainResponse } from "../../types"; import type * as schemas from "../../types/schemas"; -import { ClientCache, ExecuteResponse } from "../base"; +import { ClientCache, ExecuteResponse } from "../AbstractBaseClient"; +import { AbstractTraineeClient } from "../AbstractTraineeClient"; import { HowsoError, RequiredError } from "../errors"; -import { TraineeClient } from "../trainee"; import { batcher, BatchOptions, CacheMap } from "../utilities"; -import { FileSystemClient } from "./files"; +import { AbstractFileSystem } from "./filesystem"; export interface ClientOptions { trace?: boolean; @@ -22,13 +23,13 @@ export interface ClientOptions { migrationsUrl?: string | URL; } -export class HowsoWorkerClient extends TraineeClient { - public readonly fs: FileSystemClient; +export class HowsoWorkerClient extends AbstractTraineeClient { protected activeSession?: Session; protected cache: CacheMap>; constructor( - protected readonly worker: Worker, + protected readonly worker: Worker | NodeWorker, + public readonly fs: AbstractFileSystem, protected readonly options: ClientOptions, ) { super(); @@ -39,7 +40,6 @@ export class HowsoWorkerClient extends TraineeClient { throw new RequiredError("options", "Client options are required."); } this.cache = new CacheMap(); - this.fs = new FileSystemClient(this.worker); } /** @@ -102,7 +102,10 @@ export class HowsoWorkerClient extends TraineeClient { reject(); } }; - this.worker.postMessage(request, [channel.port2]); + this.worker.postMessage(request, [ + // @ts-expect-error The port will match browser/nodejs depending on the context + channel.port2, + ]); }); } diff --git a/src/client/worker/files.ts b/src/client/worker/filesystem/AbstractFileSystem.ts similarity index 87% rename from src/client/worker/files.ts rename to src/client/worker/filesystem/AbstractFileSystem.ts index 665040d..690d2a4 100644 --- a/src/client/worker/files.ts +++ b/src/client/worker/filesystem/AbstractFileSystem.ts @@ -5,19 +5,13 @@ import type { FileSystemResponseBody, IFileSystem, } from "@howso/amalgam-lang"; -import { isNode } from "../utilities"; +import type { Worker as NodeWorker } from "node:worker_threads"; -export class FileSystemClient implements IFileSystem { - protected readonly baseDir: string; +export abstract class AbstractFileSystem implements IFileSystem { + protected abstract readonly baseDir: string; + protected abstract readonly worker: T; public readonly entityExt = "caml"; - constructor( - private readonly worker: Worker, - baseDir?: string, - ) { - this.baseDir = baseDir ?? "/app/"; - } - public get libDir(): string { return this.baseDir; } @@ -30,6 +24,8 @@ export class FileSystemClient implements IFileSystem { return this.join(this.libDir, "migrations", "/"); } + public abstract prepareFile(parent: string, name: string, url: string): Promise; + public join(...parts: string[]): string { const segments = []; if (parts?.[0]?.startsWith("/")) { @@ -59,20 +55,13 @@ export class FileSystemClient implements IFileSystem { reject(ev.data?.error); } }; - this.worker.postMessage(request, [channel.port2]); + this.worker.postMessage(request, [ + // @ts-expect-error The port will match browser/nodejs depending on the context + channel.port2, + ]); }); } - public async prepareFile(parent: string, name: string, url: string): Promise { - if (isNode) { - const { readFile } = await import("node:fs/promises"); - const data = await readFile(url); - this.writeFile(this.join(parent, name), data); - } else { - this.createLazyFile(parent, name, url); - } - } - public async analyzePath(path: string, dontResolveLastLink?: boolean): Promise { return await this.dispatch({ type: "request", diff --git a/src/client/worker/filesystem/BrowserFileSystem.ts b/src/client/worker/filesystem/BrowserFileSystem.ts new file mode 100644 index 0000000..eaf436e --- /dev/null +++ b/src/client/worker/filesystem/BrowserFileSystem.ts @@ -0,0 +1,16 @@ +import { AbstractFileSystem } from "./AbstractFileSystem"; + +export class BrowserFileSystem extends AbstractFileSystem { + protected readonly baseDir: string; + protected readonly worker: Worker; + + constructor(worker: Worker, baseDir?: string) { + super(); + this.worker = worker; + this.baseDir = baseDir ?? "/app/"; + } + + public async prepareFile(parent: string, name: string, url: string): Promise { + return await this.createLazyFile(parent, name, url); + } +} diff --git a/src/client/worker/filesystem/NodeFileSystem.ts b/src/client/worker/filesystem/NodeFileSystem.ts new file mode 100644 index 0000000..b4cbf16 --- /dev/null +++ b/src/client/worker/filesystem/NodeFileSystem.ts @@ -0,0 +1,23 @@ +import type { Worker } from "node:worker_threads"; +import { isNode } from "../../utilities"; +import { AbstractFileSystem } from "./AbstractFileSystem"; + +export class NodeFileSystem extends AbstractFileSystem { + protected readonly baseDir: string; + protected readonly worker: Worker; + + constructor(worker: Worker, baseDir?: string) { + super(); + if (!isNode) { + throw new Error("NodeFileSystem is only valid in Node contexts."); + } + this.worker = worker; + this.baseDir = baseDir ?? "/app/"; + } + + public async prepareFile(parent: string, name: string, url: string): Promise { + const { readFile } = await import("node:fs/promises"); + const data = await readFile(url); + this.writeFile(this.join(parent, name), data); + } +} diff --git a/src/client/worker/filesystem/index.ts b/src/client/worker/filesystem/index.ts new file mode 100644 index 0000000..f1553d1 --- /dev/null +++ b/src/client/worker/filesystem/index.ts @@ -0,0 +1,3 @@ +export * from "./AbstractFileSystem"; +export * from "./BrowserFileSystem"; +export * from "./NodeFileSystem"; diff --git a/src/client/worker/index.ts b/src/client/worker/index.ts index 7437baf..a01568f 100644 --- a/src/client/worker/index.ts +++ b/src/client/worker/index.ts @@ -1,2 +1,2 @@ -export * from "./client"; -export * from "./files"; +export * from "./filesystem"; +export * from "./HowsoWorkerClient"; diff --git a/src/engine/howso.caml b/src/engine/howso.caml index 6f1a1ac..5b8c3f0 100644 Binary files a/src/engine/howso.caml and b/src/engine/howso.caml differ diff --git a/src/tests/assets/NodeWorker.js b/src/tests/assets/NodeWorker.js new file mode 100644 index 0000000..98db1b6 --- /dev/null +++ b/src/tests/assets/NodeWorker.js @@ -0,0 +1,25 @@ +import { AmalgamWasmService, initRuntime } from "@howso/amalgam-lang"; +import { createRequire } from "node:module"; +import { parentPort } from "node:worker_threads"; +const require = createRequire(import.meta.url); +const wasmDataUri = require.resolve("@howso/amalgam-lang/lib/amalgam-st.data"); +const wasmUri = require.resolve("@howso/amalgam-lang/lib/amalgam-st.wasm"); + +(async function () { + const svc = new AmalgamWasmService((options) => { + return initRuntime(options, { + locateFile: (path) => { + if (path.endsWith("amalgam-st.wasm")) { + return wasmUri; + } else if (path.endsWith("amalgam-st.data")) { + return wasmDataUri; + } + return self.location.href + path; + }, + }); + }); + parentPort.onmessage = async (ev) => { + svc.dispatch(ev); + }; + parentPort.postMessage({ type: "event", event: "ready" }); +})(); diff --git a/src/tests/assets/iris.json b/src/tests/assets/iris.json new file mode 100644 index 0000000..208057f --- /dev/null +++ b/src/tests/assets/iris.json @@ -0,0 +1 @@ +{"columns":["sepal_length","sepal_width","petal_length","petal_width","class"],"data":[[5.1,3.5,1.4,0.2,"Iris-setosa"],[4.9,3.0,1.4,0.2,"Iris-setosa"],[4.7,3.2,1.3,0.2,"Iris-setosa"],[4.6,3.1,1.5,0.2,"Iris-setosa"],[5.0,3.6,1.4,0.2,"Iris-setosa"],[5.4,3.9,1.7,0.4,"Iris-setosa"],[4.6,3.4,1.4,0.3,"Iris-setosa"],[5.0,3.4,1.5,0.2,"Iris-setosa"],[4.4,2.9,1.4,0.2,"Iris-setosa"],[4.9,3.1,1.5,0.1,"Iris-setosa"],[5.4,3.7,1.5,0.2,"Iris-setosa"],[4.8,3.4,1.6,0.2,"Iris-setosa"],[4.8,3.0,1.4,0.1,"Iris-setosa"],[4.3,3.0,1.1,0.1,"Iris-setosa"],[5.8,4.0,1.2,0.2,"Iris-setosa"],[5.7,4.4,1.5,0.4,"Iris-setosa"],[5.4,3.9,1.3,0.4,"Iris-setosa"],[5.1,3.5,1.4,0.3,"Iris-setosa"],[5.7,3.8,1.7,0.3,"Iris-setosa"],[5.1,3.8,1.5,0.3,"Iris-setosa"],[5.4,3.4,1.7,0.2,"Iris-setosa"],[5.1,3.7,1.5,0.4,"Iris-setosa"],[4.6,3.6,1.0,0.2,"Iris-setosa"],[5.1,3.3,1.7,0.5,"Iris-setosa"],[4.8,3.4,1.9,0.2,"Iris-setosa"],[5.0,3.0,1.6,0.2,"Iris-setosa"],[5.0,3.4,1.6,0.4,"Iris-setosa"],[5.2,3.5,1.5,0.2,"Iris-setosa"],[5.2,3.4,1.4,0.2,"Iris-setosa"],[4.7,3.2,1.6,0.2,"Iris-setosa"],[4.8,3.1,1.6,0.2,"Iris-setosa"],[5.4,3.4,1.5,0.4,"Iris-setosa"],[5.2,4.1,1.5,0.1,"Iris-setosa"],[5.5,4.2,1.4,0.2,"Iris-setosa"],[4.9,3.1,1.5,0.1,"Iris-setosa"],[5.0,3.2,1.2,0.2,"Iris-setosa"],[5.5,3.5,1.3,0.2,"Iris-setosa"],[4.9,3.1,1.5,0.1,"Iris-setosa"],[4.4,3.0,1.3,0.2,"Iris-setosa"],[5.1,3.4,1.5,0.2,"Iris-setosa"],[5.0,3.5,1.3,0.3,"Iris-setosa"],[4.5,2.3,1.3,0.3,"Iris-setosa"],[4.4,3.2,1.3,0.2,"Iris-setosa"],[5.0,3.5,1.6,0.6,"Iris-setosa"],[5.1,3.8,1.9,0.4,"Iris-setosa"],[4.8,3.0,1.4,0.3,"Iris-setosa"],[5.1,3.8,1.6,0.2,"Iris-setosa"],[4.6,3.2,1.4,0.2,"Iris-setosa"],[5.3,3.7,1.5,0.2,"Iris-setosa"],[5.0,3.3,1.4,0.2,"Iris-setosa"],[7.0,3.2,4.7,1.4,"Iris-versicolor"],[6.4,3.2,4.5,1.5,"Iris-versicolor"],[6.9,3.1,4.9,1.5,"Iris-versicolor"],[5.5,2.3,4.0,1.3,"Iris-versicolor"],[6.5,2.8,4.6,1.5,"Iris-versicolor"],[5.7,2.8,4.5,1.3,"Iris-versicolor"],[6.3,3.3,4.7,1.6,"Iris-versicolor"],[4.9,2.4,3.3,1.0,"Iris-versicolor"],[6.6,2.9,4.6,1.3,"Iris-versicolor"],[5.2,2.7,3.9,1.4,"Iris-versicolor"],[5.0,2.0,3.5,1.0,"Iris-versicolor"],[5.9,3.0,4.2,1.5,"Iris-versicolor"],[6.0,2.2,4.0,1.0,"Iris-versicolor"],[6.1,2.9,4.7,1.4,"Iris-versicolor"],[5.6,2.9,3.6,1.3,"Iris-versicolor"],[6.7,3.1,4.4,1.4,"Iris-versicolor"],[5.6,3.0,4.5,1.5,"Iris-versicolor"],[5.8,2.7,4.1,1.0,"Iris-versicolor"],[6.2,2.2,4.5,1.5,"Iris-versicolor"],[5.6,2.5,3.9,1.1,"Iris-versicolor"],[5.9,3.2,4.8,1.8,"Iris-versicolor"],[6.1,2.8,4.0,1.3,"Iris-versicolor"],[6.3,2.5,4.9,1.5,"Iris-versicolor"],[6.1,2.8,4.7,1.2,"Iris-versicolor"],[6.4,2.9,4.3,1.3,"Iris-versicolor"],[6.6,3.0,4.4,1.4,"Iris-versicolor"],[6.8,2.8,4.8,1.4,"Iris-versicolor"],[6.7,3.0,5.0,1.7,"Iris-versicolor"],[6.0,2.9,4.5,1.5,"Iris-versicolor"],[5.7,2.6,3.5,1.0,"Iris-versicolor"],[5.5,2.4,3.8,1.1,"Iris-versicolor"],[5.5,2.4,3.7,1.0,"Iris-versicolor"],[5.8,2.7,3.9,1.2,"Iris-versicolor"],[6.0,2.7,5.1,1.6,"Iris-versicolor"],[5.4,3.0,4.5,1.5,"Iris-versicolor"],[6.0,3.4,4.5,1.6,"Iris-versicolor"],[6.7,3.1,4.7,1.5,"Iris-versicolor"],[6.3,2.3,4.4,1.3,"Iris-versicolor"],[5.6,3.0,4.1,1.3,"Iris-versicolor"],[5.5,2.5,4.0,1.3,"Iris-versicolor"],[5.5,2.6,4.4,1.2,"Iris-versicolor"],[6.1,3.0,4.6,1.4,"Iris-versicolor"],[5.8,2.6,4.0,1.2,"Iris-versicolor"],[5.0,2.3,3.3,1.0,"Iris-versicolor"],[5.6,2.7,4.2,1.3,"Iris-versicolor"],[5.7,3.0,4.2,1.2,"Iris-versicolor"],[5.7,2.9,4.2,1.3,"Iris-versicolor"],[6.2,2.9,4.3,1.3,"Iris-versicolor"],[5.1,2.5,3.0,1.1,"Iris-versicolor"],[5.7,2.8,4.1,1.3,"Iris-versicolor"],[6.3,3.3,6.0,2.5,"Iris-virginica"],[5.8,2.7,5.1,1.9,"Iris-virginica"],[7.1,3.0,5.9,2.1,"Iris-virginica"],[6.3,2.9,5.6,1.8,"Iris-virginica"],[6.5,3.0,5.8,2.2,"Iris-virginica"],[7.6,3.0,6.6,2.1,"Iris-virginica"],[4.9,2.5,4.5,1.7,"Iris-virginica"],[7.3,2.9,6.3,1.8,"Iris-virginica"],[6.7,2.5,5.8,1.8,"Iris-virginica"],[7.2,3.6,6.1,2.5,"Iris-virginica"],[6.5,3.2,5.1,2.0,"Iris-virginica"],[6.4,2.7,5.3,1.9,"Iris-virginica"],[6.8,3.0,5.5,2.1,"Iris-virginica"],[5.7,2.5,5.0,2.0,"Iris-virginica"],[5.8,2.8,5.1,2.4,"Iris-virginica"],[6.4,3.2,5.3,2.3,"Iris-virginica"],[6.5,3.0,5.5,1.8,"Iris-virginica"],[7.7,3.8,6.7,2.2,"Iris-virginica"],[7.7,2.6,6.9,2.3,"Iris-virginica"],[6.0,2.2,5.0,1.5,"Iris-virginica"],[6.9,3.2,5.7,2.3,"Iris-virginica"],[5.6,2.8,4.9,2.0,"Iris-virginica"],[7.7,2.8,6.7,2.0,"Iris-virginica"],[6.3,2.7,4.9,1.8,"Iris-virginica"],[6.7,3.3,5.7,2.1,"Iris-virginica"],[7.2,3.2,6.0,1.8,"Iris-virginica"],[6.2,2.8,4.8,1.8,"Iris-virginica"],[6.1,3.0,4.9,1.8,"Iris-virginica"],[6.4,2.8,5.6,2.1,"Iris-virginica"],[7.2,3.0,5.8,1.6,"Iris-virginica"],[7.4,2.8,6.1,1.9,"Iris-virginica"],[7.9,3.8,6.4,2.0,"Iris-virginica"],[6.4,2.8,5.6,2.2,"Iris-virginica"],[6.3,2.8,5.1,1.5,"Iris-virginica"],[6.1,2.6,5.6,1.4,"Iris-virginica"],[7.7,3.0,6.1,2.3,"Iris-virginica"],[6.3,3.4,5.6,2.4,"Iris-virginica"],[6.4,3.1,5.5,1.8,"Iris-virginica"],[6.0,3.0,4.8,1.8,"Iris-virginica"],[6.9,3.1,5.4,2.1,"Iris-virginica"],[6.7,3.1,5.6,2.4,"Iris-virginica"],[6.9,3.1,5.1,2.3,"Iris-virginica"],[5.8,2.7,5.1,1.9,"Iris-virginica"],[6.8,3.2,5.9,2.3,"Iris-virginica"],[6.7,3.3,5.7,2.5,"Iris-virginica"],[6.7,3.0,5.2,2.3,"Iris-virginica"],[6.3,2.5,5.0,1.9,"Iris-virginica"],[6.5,3.0,5.2,2.0,"Iris-virginica"],[6.2,3.4,5.4,2.3,"Iris-virginica"],[5.9,3.0,5.1,1.8,"Iris-virginica"]]} \ No newline at end of file diff --git a/src/types/features.ts b/src/types/features.ts deleted file mode 100644 index f902d4b..0000000 --- a/src/types/features.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { EditHistory } from "./schemas"; - -/** - * Built in features and their respective value type. These features may be automatically computed to a case or found - * in react details. - */ -export type BuiltInFeatures = { - ".case_edit_history"?: EditHistory | null; - ".case_weight"?: number | null; - ".session"?: string; - ".session_training_index"?: number; - ".imputed"?: string[] | null; - ".influence_weight"?: number | null; - ".raw_influence_weight"?: number | null; - ".series_progress"?: number | null; - ".series_progress_delta"?: number | null; - ".series_index"?: number | null; -}; diff --git a/src/types/schemas/BuiltInFeatures.ts b/src/types/schemas/BuiltInFeatures.ts new file mode 100644 index 0000000..1e32150 --- /dev/null +++ b/src/types/schemas/BuiltInFeatures.ts @@ -0,0 +1,48 @@ +/** + * WARNING: This file is auto generated, do not modify manually. + * + * BuiltInFeatures + */ +import type { EditHistory } from "./EditHistory"; + +export type BuiltInFeatures = { + ".case_edit_history"?: EditHistory; + /* + * The default feature to store case weight, which scales the influence of a case during queries. + */ + ".case_weight"?: number | null; + /* + * The list of features that have been imputed for the case. + */ + ".imputed"?: any[] | null; + /* + * The normalized influence of a case on a prediction. + */ + ".influence_weight"?: number; + /* + * The influence of a case on a prediction. + */ + ".raw_influence_weight"?: number; + /* + * The zero-based index of the case within its series sorted by time. + */ + ".series_index"?: number; + /* + * A 0-1 float describing the progress though the case's series at the point in time described by the case. + */ + ".series_progress"?: number; + /* + * The delta between the series progress of the previous case of the series and the case being described. + */ + ".series_progress_delta"?: number; + /* + * The name of the session the case was trained in. + */ + ".session"?: string; + /* + * The 0-based index of the case's order in training within its session. + */ + ".session_training_index"?: number; +} & Record<`${string}_delta_${number}`, number | null> & + Record<`${string}_lag_${number}`, any> & + Record<`${string}_rate_${number}`, number | null>; diff --git a/src/types/schemas/FeatureAttributes.ts b/src/types/schemas/FeatureAttributes.ts index af39fc0..76a6b14 100644 --- a/src/types/schemas/FeatureAttributes.ts +++ b/src/types/schemas/FeatureAttributes.ts @@ -74,6 +74,10 @@ export type FeatureAttributes = { * The date time format locale. if unspecified, uses platform default locale. */ locale?: string; + /* + * The number of time steps traced back by the maximum lag feature created for this feature. + */ + max_row_lag?: number; /* * Flag a categorical nominal feature as non-sensitive. it is recommended that all nominal features be represented with either an `int-id` subtype or another available nominal subtype using the `subtype` attribute. however, if the nominal feature is non-sensitive, setting this parameter to true will bypass the `subtype` requirement. only applicable to nominal features. */ @@ -103,10 +107,22 @@ export type FeatureAttributes = { */ data_type: "object" | "string" | "numeric" | "integer" | "boolean" | "datetime" | "time" | "date" | "timedelta"; } & Record; + /* + * The feature whose values this time-series feature's values are derived from. + */ + parent?: string; + /* + * The type of time-series processing used by the parent feature. + */ + parent_type?: "delta" | "rate"; /* * Custom amalgam code that is called on resulting values of this feature during react operations. */ post_process?: string; + /* + * A sample of a value for the feature. + */ + sample?: any; /* * Round to the specified significant digits, default is no rounding. */ @@ -166,12 +182,20 @@ export type FeatureAttributes = { /* * When `rate` is specified, uses the difference of the current value from its previous value divided by the change in time since the previous value. when `delta` is specified, uses the difference of the current value from its previous value regardless of the elapsed time. set to `delta` if feature has `time_feature` set to true. */ - type: "rate" | "delta"; + type?: "rate" | "delta"; /* * Controls whether future values of independent time series are considered. applicable only to the time feature. when false, the time feature is not universal and allows using future data from other series in decisions; this is applicable when the time is not globally relevant and is independent for each time series. when true, universally excludes using any data with from the future from all series; this is applicable when time is globally relevant and there are events that may affect all time series. if there is any possibility of global relevancy of time, it is generally recommended to set this value to true, which is the default. */ universal?: boolean; }; + /* + * The order of rate/delta being described by this time-series feature. + */ + ts_order?: number; + /* + * The type of value being captured by this time-series feature. + */ + ts_type?: "lag" | "delta" | "rate"; /* * The type of the feature. * @@ -184,4 +208,4 @@ export type FeatureAttributes = { * Flag feature as only having unique values. only applicable to nominals features. */ unique?: boolean; -} & Record; +}; diff --git a/src/types/schemas/index.ts b/src/types/schemas/index.ts index ecf3f9d..069e3cc 100644 --- a/src/types/schemas/index.ts +++ b/src/types/schemas/index.ts @@ -4,6 +4,7 @@ export * from "./AddFeature"; export * from "./Analyze"; export * from "./AppendToSeriesStore"; +export * from "./BuiltInFeatures"; export * from "./CaseIndices"; export * from "./ClearImputedData"; export * from "./Condition";