functionalscript 0.12.7 → 0.12.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.
package/io/module.f.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Effect } from '../types/effects/module.f.ts';
2
- import type { ExecResult, Headers, IoResult, NodeOp } from '../types/effects/node/module.f.ts';
2
+ import type { Headers, NodeOp } from '../types/effects/node/module.f.ts';
3
3
  import { type Result } from '../types/result/module.f.ts';
4
4
  /**
5
5
  * Represents a directory entry (file or directory) in the filesystem
@@ -97,7 +97,11 @@ export type Http = {
97
97
  readonly createServer: (_: RequestListener) => Server;
98
98
  };
99
99
  export type ChildProcess = {
100
- readonly exec: (command: string) => Promise<IoResult<ExecResult>>;
100
+ readonly exec: (command: string, callback: (error: unknown, stdout: string, stderr: string) => void) => {
101
+ readonly stdin: null | {
102
+ readonly end: (data?: string) => void;
103
+ };
104
+ };
101
105
  };
102
106
  /**
103
107
  * Core IO operations interface providing access to system resources
@@ -128,4 +132,4 @@ export type Run = (f: App) => Promise<never>;
128
132
  */
129
133
  export declare const run: (io: Io) => Run;
130
134
  export type EffectToPromise = <T>(effect: Effect<NodeOp, T>) => Promise<T>;
131
- export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm } }, fetch, http: { createServer }, childProcess: { exec }, }: Io) => EffectToPromise;
135
+ export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm } }, fetch, http: { createServer }, childProcess, }: Io) => EffectToPromise;
package/io/module.f.js CHANGED
@@ -35,7 +35,7 @@ const collect = async (v) => {
35
35
  }
36
36
  return result;
37
37
  };
38
- export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm } }, fetch, http: { createServer }, childProcess: { exec }, }) => {
38
+ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm } }, fetch, http: { createServer }, childProcess, }) => {
39
39
  const result = asyncRun({
40
40
  all: async (effects) => await Promise.all(effects.map(result)),
41
41
  error: async (message) => error(message),
@@ -53,7 +53,10 @@ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readF
53
53
  .map(v => ({ name: v.name, parentPath: normalize(v.parentPath), isFile: v.isFile() }))),
54
54
  writeFile: ([path, data]) => tc(() => writeFile(path, fromVec(data))),
55
55
  rm: path => tc(() => rm(path)),
56
- exec,
56
+ exec: ([command, stdin]) => new Promise(resolve => {
57
+ const child = childProcess.exec(command, (e, stdout, stderr) => resolve(e !== null ? ['error', e] : ok({ stdout, stderr })));
58
+ child.stdin?.end(stdin);
59
+ }),
57
60
  createServer: async (requestListener) => {
58
61
  const erl = requestListener;
59
62
  const nodeRl = async (req, res) => {
package/io/module.js CHANGED
@@ -7,10 +7,10 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  return path;
8
8
  };
9
9
  import http from 'node:http';
10
- import { exec } from 'node:child_process';
11
- import { fromIo, run } from "./module.f.js";
10
+ import childProcess from 'node:child_process';
12
11
  import fs from 'node:fs';
13
12
  import process from 'node:process';
13
+ import { fromIo, run } from "./module.f.js";
14
14
  import { concat } from "../path/module.f.js";
15
15
  import { error, ok } from "../types/result/module.f.js";
16
16
  const prefix = 'file:///';
@@ -42,9 +42,7 @@ export const io = {
42
42
  }
43
43
  },
44
44
  http,
45
- childProcess: {
46
- exec: command => new Promise(resolve => exec(command, (e, stdout, stderr) => resolve(e !== null ? error(e) : ok({ stdout, stderr })))),
47
- },
45
+ childProcess,
48
46
  };
49
47
  export const legacyRun = run(io);
50
48
  export const ioRun = (io) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.12.7",
3
+ "version": "0.12.9",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -47,8 +47,9 @@ export type ExecResult = {
47
47
  readonly stdout: string;
48
48
  readonly stderr: string;
49
49
  };
50
- export type Exec = readonly ['exec', (_: string) => IoResult<ExecResult>];
51
- export declare const exec: Func<Exec>;
50
+ export type ExecParam = readonly [string, string?];
51
+ export type Exec = readonly ['exec', (_: ExecParam) => IoResult<ExecResult>];
52
+ export declare const exec: RestFunc<Exec>;
52
53
  export type Fs = Mkdir | ReadFile | Readdir | WriteFile | Rm | Exec;
53
54
  export type Error = ['error', (_: string) => void];
54
55
  export declare const error: Func<Error>;
@@ -15,7 +15,7 @@ export const readFile = do_('readFile');
15
15
  export const readdir = doRest('readdir');
16
16
  export const writeFile = doRest('writeFile');
17
17
  export const rm = do_('rm');
18
- export const exec = do_('exec');
18
+ export const exec = doRest('exec');
19
19
  export const error = do_('error');
20
20
  export const log = do_('log');
21
21
  export const createServer = do_('createServer');
@@ -64,4 +64,6 @@ export type Or<T extends readonly Type[]> = () => readonly ['or', ...T];
64
64
  export declare const or: <T extends readonly Type[]>(...types: T) => Or<T>;
65
65
  /** Constructs a schema that validates a value matching `T` or `undefined`. */
66
66
  export declare const option: <T extends Type>(t: T) => Or<readonly [T, undefined]>;
67
+ /** Schema that never matches any value — the empty union, corresponding to TypeScript's `never`. */
68
+ export declare const never: Or<readonly []>;
67
69
  export {};
@@ -23,3 +23,5 @@ export const record = type1('record');
23
23
  export const or = (...types) => () => ['or', ...types];
24
24
  /** Constructs a schema that validates a value matching `T` or `undefined`. */
25
25
  export const option = (t) => or(t, undefined);
26
+ /** Schema that never matches any value — the empty union, corresponding to TypeScript's `never`. */
27
+ export const never = or();
@@ -31,12 +31,36 @@ export type StructTs<T extends Struct> = {
31
31
  *
32
32
  * @example
33
33
  * ```ts
34
- * type A = Ts<typeof string> // string
35
- * type B = Ts<4> // 4
36
- * type C = Ts<Array<typeof number>> // readonly number[]
34
+ * type A = Ts<typeof string> // string
35
+ * type B = Ts<4> // 4
36
+ * type C = Ts<Array<typeof number>> // readonly number[]
37
37
  * type D = Ts<{ x: typeof boolean }> // { readonly x: boolean }
38
38
  * ```
39
39
  */
40
40
  export type Ts<T extends Type> = T extends () => infer I ? (I extends readonly ['const', infer C] ? ConstTs<C> : I extends readonly ['boolean'] ? boolean : I extends readonly ['number'] ? number : I extends readonly ['string'] ? string : I extends readonly ['bigint'] ? bigint : I extends readonly ['unknown'] ? DjsUnknown : I extends readonly ['array', infer E extends Type] ? readonly Ts<E>[] : I extends readonly ['record', infer E extends Type] ? {
41
41
  readonly [K in string]: Ts<E>;
42
42
  } : I extends readonly ['or', ...infer A extends readonly Type[]] ? Ts<A[number]> : never) : ConstTs<T>;
43
+ /**
44
+ * Converts an RTTI schema `Type` to its TypeScript type expression as a string.
45
+ *
46
+ * Mirrors the compile-time `Ts<T>` mapped type at runtime.
47
+ *
48
+ * **Note:** recursive schemas (e.g. `const list = () => ['array', list] as const`)
49
+ * will cause infinite recursion. Only acyclic schemas are supported.
50
+ *
51
+ * **Note:** the `unknown` schema produces the string `'unknown'` (TypeScript's built-in),
52
+ * whereas `Ts<>` maps it to `DjsUnknown` from `djs/module.f.ts`.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * toTs(boolean) // 'boolean'
57
+ * toTs(array(number)) // 'readonly(number)[]'
58
+ * toTs(record(string)) // '{readonly[k in string]:string}'
59
+ * toTs(or(string, number)) // 'string|number'
60
+ * toTs(42) // '42'
61
+ * toTs('hello') // '"hello"'
62
+ * toTs([boolean, number]) // 'readonly[boolean,number]'
63
+ * toTs({ x: string }) // '{readonly "x":string}'
64
+ * ```
65
+ */
66
+ export declare const toTs: (rtti: Type) => string;
@@ -1 +1,50 @@
1
- export {};
1
+ /**
2
+ * TypeScript type transformers for RTTI schemas.
3
+ *
4
+ * Each `*Ts` type maps a schema (or schema fragment) to its corresponding TypeScript type.
5
+ * The main entry point is `Ts<T>`.
6
+ *
7
+ * The runtime `toTs` function mirrors `Ts<T>` at value level, returning a TypeScript
8
+ * type expression string for a given RTTI schema.
9
+ */
10
+ import { primitive, tuple, struct, array, record, union } from "../../ts/module.f.js";
11
+ /** Serialises a `Const` schema to its TypeScript type expression. */
12
+ const constToTs = (rtti) => typeof rtti !== 'object' || rtti === null ? primitive(rtti) :
13
+ rtti instanceof Array ? tuple(rtti.map(toTs)) :
14
+ struct(Object.entries(rtti).map(([k, v]) => [k, toTs(v)]));
15
+ /**
16
+ * Converts an RTTI schema `Type` to its TypeScript type expression as a string.
17
+ *
18
+ * Mirrors the compile-time `Ts<T>` mapped type at runtime.
19
+ *
20
+ * **Note:** recursive schemas (e.g. `const list = () => ['array', list] as const`)
21
+ * will cause infinite recursion. Only acyclic schemas are supported.
22
+ *
23
+ * **Note:** the `unknown` schema produces the string `'unknown'` (TypeScript's built-in),
24
+ * whereas `Ts<>` maps it to `DjsUnknown` from `djs/module.f.ts`.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * toTs(boolean) // 'boolean'
29
+ * toTs(array(number)) // 'readonly(number)[]'
30
+ * toTs(record(string)) // '{readonly[k in string]:string}'
31
+ * toTs(or(string, number)) // 'string|number'
32
+ * toTs(42) // '42'
33
+ * toTs('hello') // '"hello"'
34
+ * toTs([boolean, number]) // 'readonly[boolean,number]'
35
+ * toTs({ x: string }) // '{readonly "x":string}'
36
+ * ```
37
+ */
38
+ export const toTs = (rtti) => {
39
+ if (typeof rtti !== 'function') {
40
+ return constToTs(rtti);
41
+ }
42
+ const [tag, ...rest] = rtti();
43
+ switch (tag) {
44
+ case 'const': return constToTs(rest[0]);
45
+ case 'array': return array(toTs(rest[0]));
46
+ case 'record': return record(toTs(rest[0]));
47
+ case 'or': return union(rest.map(toTs));
48
+ default: return tag; // tag0: 'boolean' | 'number' | 'string' | 'bigint' | 'unknown'
49
+ }
50
+ };
@@ -0,0 +1,53 @@
1
+ declare const _default: {
2
+ tag0: {
3
+ boolean: () => void;
4
+ number: () => void;
5
+ string: () => void;
6
+ bigint: () => void;
7
+ unknown: () => void;
8
+ };
9
+ tag1: {
10
+ array: {
11
+ primitive: () => void;
12
+ nested: () => void;
13
+ union: () => void;
14
+ };
15
+ record: {
16
+ primitive: () => void;
17
+ nested: () => void;
18
+ };
19
+ };
20
+ const: {
21
+ null: () => void;
22
+ undefined: () => void;
23
+ true: () => void;
24
+ false: () => void;
25
+ number: () => void;
26
+ nan: () => void;
27
+ inf: () => void;
28
+ negInf: () => void;
29
+ string: () => void;
30
+ bigint: () => void;
31
+ emptyTuple: () => void;
32
+ tuple: () => void;
33
+ emptyStruct: () => void;
34
+ struct: () => void;
35
+ nestedStruct: () => void;
36
+ quotedKey: () => void;
37
+ stringWithQuote: () => void;
38
+ keyWithQuote: () => void;
39
+ };
40
+ constThunk: {
41
+ primitive: () => void;
42
+ string: () => void;
43
+ };
44
+ or: {
45
+ empty: () => void;
46
+ consts: () => void;
47
+ thunks: () => void;
48
+ mixed: () => void;
49
+ };
50
+ never: () => void;
51
+ option: () => void;
52
+ };
53
+ export default _default;
@@ -0,0 +1,60 @@
1
+ import { toTs } from "./module.f.js";
2
+ import { boolean, number, string, bigint, unknown, array, record, or, option, never } from "../module.f.js";
3
+ const eq = (rtti, expected) => {
4
+ const result = toTs(rtti);
5
+ if (result !== expected) {
6
+ throw `expected ${JSON.stringify(expected)}, got ${JSON.stringify(result)}`;
7
+ }
8
+ };
9
+ export default {
10
+ tag0: {
11
+ boolean: () => eq(boolean, 'boolean'),
12
+ number: () => eq(number, 'number'),
13
+ string: () => eq(string, 'string'),
14
+ bigint: () => eq(bigint, 'bigint'),
15
+ unknown: () => eq(unknown, 'unknown'),
16
+ },
17
+ tag1: {
18
+ array: {
19
+ primitive: () => eq(array(number), 'readonly(number)[]'),
20
+ nested: () => eq(array(array(boolean)), 'readonly(readonly(boolean)[])[]'),
21
+ union: () => eq(array(or(number, string)), 'readonly(number|string)[]'),
22
+ },
23
+ record: {
24
+ primitive: () => eq(record(string), '{readonly[k in string]:string}'),
25
+ nested: () => eq(record(record(number)), '{readonly[k in string]:{readonly[k in string]:number}}'),
26
+ },
27
+ },
28
+ const: {
29
+ null: () => eq(null, 'null'),
30
+ undefined: () => eq(undefined, 'undefined'),
31
+ true: () => eq(true, 'true'),
32
+ false: () => eq(false, 'false'),
33
+ number: () => eq(42, '42'),
34
+ nan: () => eq(NaN, 'number'),
35
+ inf: () => eq(Infinity, 'number'),
36
+ negInf: () => eq(-Infinity, 'number'),
37
+ string: () => eq('hello', '"hello"'),
38
+ bigint: () => eq(7n, '7n'),
39
+ emptyTuple: () => eq([], 'readonly[]'),
40
+ tuple: () => eq([12, true], 'readonly[12,true]'),
41
+ emptyStruct: () => eq({}, '{}'),
42
+ struct: () => eq({ a: number, b: string }, '{readonly"a":number,readonly"b":string}'),
43
+ nestedStruct: () => eq({ x: { y: boolean } }, '{readonly"x":{readonly"y":boolean}}'),
44
+ quotedKey: () => eq({ 'my-key': number }, '{readonly"my-key":number}'),
45
+ stringWithQuote: () => eq('say "hi"', '"say \\"hi\\""'),
46
+ keyWithQuote: () => eq({ 'a"b': number }, '{readonly"a\\"b":number}'),
47
+ },
48
+ constThunk: {
49
+ primitive: () => eq(() => ['const', 42n], '42n'),
50
+ string: () => eq(() => ['const', 'hi'], '"hi"'),
51
+ },
52
+ or: {
53
+ empty: () => eq(or(), 'never'),
54
+ consts: () => eq(or(false, 42, 'hello'), 'false|42|"hello"'),
55
+ thunks: () => eq(or(number, string), 'number|string'),
56
+ mixed: () => eq(or(42, string), '42|string'),
57
+ },
58
+ never: () => eq(never, 'never'),
59
+ option: () => eq(option(number), 'number|undefined'),
60
+ };
@@ -3,7 +3,6 @@ import { error, ok } from "../../result/module.f.js";
3
3
  import { isArray as commonIsArray } from "../../array/module.f.js";
4
4
  import { isObject as commonIsObject } from "../../object/module.f.js";
5
5
  import { identity } from "../../function/module.f.js";
6
- import { todo } from "../../../dev/module.f.js";
7
6
  /**
8
7
  * Builds a validator for `array` or `record` schemas.
9
8
  * The inner item validator is instantiated lazily (only when the container is
@@ -1,2 +1,8 @@
1
1
  export type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false;
2
2
  export type Assert<T extends true> = T;
3
+ export declare const tuple: (types: readonly string[]) => string;
4
+ export declare const struct: (fields: readonly (readonly [string, string])[]) => string;
5
+ export declare const array: (type: string) => string;
6
+ export declare const record: (type: string) => string;
7
+ export declare const primitive: (c: bigint | string | undefined | boolean | number | null) => string;
8
+ export declare const union: (types: readonly string[]) => string;
@@ -1 +1,19 @@
1
- export {};
1
+ const complex = (open, close) => (i) => `${open}${i.join(',')}${close}`;
2
+ export const tuple = complex('readonly[', ']');
3
+ const structX = complex('{', '}');
4
+ export const struct = (fields) => structX(fields.map(([k, v]) => `readonly${JSON.stringify(k)}:${v}`));
5
+ export const array = (type) => `readonly(${type})[]`;
6
+ export const record = (type) => structX([`readonly[k in string]:${type}`]);
7
+ export const primitive = (c) => {
8
+ if (c === null) {
9
+ return 'null';
10
+ }
11
+ switch (typeof c) {
12
+ case 'bigint': return `${c}n`;
13
+ case 'string': return JSON.stringify(c);
14
+ case 'number': return isFinite(c) ? String(c) : 'number';
15
+ case 'undefined':
16
+ case 'boolean': return String(c);
17
+ }
18
+ };
19
+ export const union = (types) => types.length === 0 ? 'never' : types.join('|');