functionalscript 0.15.0 → 0.16.0

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,30 +1,63 @@
1
1
  import { images } from '../config/module.f.ts';
2
+ import { type Ts } from '../../types/rtti/ts/module.f.ts';
2
3
  export declare const os: readonly ["ubuntu", "macos", "windows"];
3
4
  export type Os = typeof os[number];
4
5
  export declare const architecture: readonly ["intel", "arm"];
5
6
  export type Architecture = typeof architecture[number];
6
7
  export type Image = typeof images[Os][Architecture];
7
- export type Step = {
8
- readonly run?: string;
9
- readonly uses?: string;
10
- readonly with?: {
11
- readonly [k: string]: string;
12
- };
13
- };
14
- export type Job = {
15
- readonly 'runs-on': Image;
16
- readonly steps: readonly Step[];
8
+ declare const stepSchema: {
9
+ readonly run: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
10
+ readonly uses: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
11
+ readonly with: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", import("../../types/rtti/module.f.ts").String>, undefined]>;
17
12
  };
18
- export type Jobs = {
19
- readonly [jobs: string]: Job;
13
+ declare const jobSchema: {
14
+ readonly 'runs-on': import("../../types/rtti/module.f.ts").String;
15
+ readonly steps: import("../../types/rtti/module.f.ts").Type1<"array", {
16
+ readonly run: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
17
+ readonly uses: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
18
+ readonly with: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", import("../../types/rtti/module.f.ts").String>, undefined]>;
19
+ }>;
20
20
  };
21
- export type GitHubAction = {
22
- readonly name: string;
21
+ declare const jobsSchema: import("../../types/rtti/module.f.ts").Type1<"record", {
22
+ readonly 'runs-on': import("../../types/rtti/module.f.ts").String;
23
+ readonly steps: import("../../types/rtti/module.f.ts").Type1<"array", {
24
+ readonly run: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
25
+ readonly uses: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
26
+ readonly with: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", import("../../types/rtti/module.f.ts").String>, undefined]>;
27
+ }>;
28
+ }>;
29
+ declare const gitHubActionSchema: {
30
+ readonly name: import("../../types/rtti/module.f.ts").String;
23
31
  readonly on: {
24
- readonly pull_request?: {};
32
+ readonly pull_request: import("../../types/rtti/module.f.ts").Or<readonly [{}, undefined]>;
25
33
  };
26
- readonly jobs: Jobs;
34
+ readonly jobs: import("../../types/rtti/module.f.ts").Type1<"record", {
35
+ readonly 'runs-on': import("../../types/rtti/module.f.ts").String;
36
+ readonly steps: import("../../types/rtti/module.f.ts").Type1<"array", {
37
+ readonly run: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
38
+ readonly uses: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
39
+ readonly with: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", import("../../types/rtti/module.f.ts").String>, undefined]>;
40
+ }>;
41
+ }>;
27
42
  };
43
+ export type Step = Ts<typeof stepSchema>;
44
+ export type Job = Ts<typeof jobSchema>;
45
+ export type Jobs = Ts<typeof jobsSchema>;
46
+ export type GitHubAction = Ts<typeof gitHubActionSchema>;
47
+ export declare const parseGitHubAction: import("../../types/rtti/parse/module.f.ts").Parse<{
48
+ readonly name: import("../../types/rtti/module.f.ts").String;
49
+ readonly on: {
50
+ readonly pull_request: import("../../types/rtti/module.f.ts").Or<readonly [{}, undefined]>;
51
+ };
52
+ readonly jobs: import("../../types/rtti/module.f.ts").Type1<"record", {
53
+ readonly 'runs-on': import("../../types/rtti/module.f.ts").String;
54
+ readonly steps: import("../../types/rtti/module.f.ts").Type1<"array", {
55
+ readonly run: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
56
+ readonly uses: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
57
+ readonly with: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", import("../../types/rtti/module.f.ts").String>, undefined]>;
58
+ }>;
59
+ }>;
60
+ }>;
28
61
  export type StepType = 'install' | 'test';
29
62
  export type MetaStep = {
30
63
  readonly type: StepType;
@@ -42,3 +75,4 @@ export declare const clean: (steps: readonly MetaStep[]) => readonly MetaStep[];
42
75
  export declare const toSteps: (m: readonly MetaStep[]) => readonly Step[];
43
76
  export declare const ubuntu: (ms: readonly MetaStep[]) => Job;
44
77
  export declare const findTgz: (v: Os) => "(Get-ChildItem *.tgz).FullName" | "./*.tgz";
78
+ export {};
@@ -1,6 +1,25 @@
1
1
  import { images, rust } from "../config/module.f.js";
2
+ import { option, array, record, string } from "../../types/rtti/module.f.js";
3
+ import {} from "../../types/rtti/ts/module.f.js";
4
+ import { parse as rttiParse } from "../../types/rtti/parse/module.f.js";
2
5
  export const os = ['ubuntu', 'macos', 'windows'];
3
6
  export const architecture = ['intel', 'arm'];
7
+ const stepSchema = {
8
+ run: option(string),
9
+ uses: option(string),
10
+ with: option(record(string))
11
+ };
12
+ const jobSchema = {
13
+ 'runs-on': string,
14
+ steps: array(stepSchema)
15
+ };
16
+ const jobsSchema = record(jobSchema);
17
+ const gitHubActionSchema = {
18
+ name: string,
19
+ on: { pull_request: option({}) },
20
+ jobs: jobsSchema
21
+ };
22
+ export const parseGitHubAction = rttiParse(gitHubActionSchema);
4
23
  export const install = (step) => ({ type: 'install', step });
5
24
  export const test = (step) => ({ type: 'test', step });
6
25
  export const clean = (steps) => [
@@ -22,4 +22,4 @@ export declare const node: {
22
22
  };
23
23
  export declare const wasmtime = "44.0.1";
24
24
  export declare const wasmer = "7.1.0";
25
- export declare const tsgo = "7.0.0-dev.20260515.1";
25
+ export declare const tsgo = "7.0.0-dev.20260517.1";
@@ -31,4 +31,4 @@ export const wasmtime = '44.0.1';
31
31
  // https://github.com/wasmerio/wasmer/releases
32
32
  export const wasmer = '7.1.0';
33
33
  // https://www.npmjs.com/package/@typescript/native-preview?activeTab=versions
34
- export const tsgo = '7.0.0-dev.20260515.1';
34
+ export const tsgo = '7.0.0-dev.20260517.1';
package/fs/ci/module.f.js CHANGED
@@ -51,7 +51,7 @@ const defaultEffect = ci({
51
51
  denoExtra: [
52
52
  test({ run: 'deno task fjs compile issues/demo/data/tree.json _tree.f.js' }),
53
53
  test({ run: 'deno task fjs t' }),
54
- test({ run: 'deno publish --dry-run' }),
54
+ test({ run: 'deno publish --dry-run --allow-slow-types' }),
55
55
  ],
56
56
  bunExtra: [
57
57
  test({ run: 'bun ./fs/fjs/module.ts t' }),
package/fs/ci/test.f.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import { ci } from "./module.f.js";
2
2
  import { utf8ToString } from "../text/module.f.js";
3
3
  import { isVec } from "../types/bit_vec/module.f.js";
4
- import { test } from "./common/module.f.js";
4
+ import { test, parseGitHubAction } from "./common/module.f.js";
5
5
  import { assert } from "../dev/module.f.js";
6
6
  import { emptyState, virtual } from "../types/effects/node/virtual/module.f.js";
7
+ import {} from "../types/rtti/ts/module.f.js";
8
+ import { parse as jsonParse } from "../json/module.f.js";
9
+ import { unwrap } from "../types/result/module.f.js";
10
+ // type Gha = Ts<typeof gitHubActionSchema>
7
11
  const hasRun = (cmd) => (gha) => Object.values(gha.jobs).some(job => job.steps.some(step => step.run?.includes(cmd)));
8
12
  const hasRunInJob = (jobId, cmd) => (gha) => gha.jobs[jobId]?.steps.some(step => step.run?.includes(cmd)) ?? false;
9
13
  const githubState = {
@@ -19,7 +23,7 @@ const run = (rust, nodeExtra = () => []) => {
19
23
  assert(workflows !== undefined && !isVec(workflows), workflows);
20
24
  const file = workflows['ci.yml'];
21
25
  assert(isVec(file), file);
22
- return JSON.parse(utf8ToString(file));
26
+ return unwrap(parseGitHubAction(jsonParse(utf8ToString(file))));
23
27
  };
24
28
  export default {
25
29
  rust: () => {
@@ -17,6 +17,6 @@ export type ModuleMap = {
17
17
  readonly [k in string]: Module;
18
18
  };
19
19
  export declare const env: (io: Io) => (v: string) => string | undefined;
20
- export declare const allFiles2: (s: string) => Effect<Readdir | All, readonly string[]>;
20
+ export declare const allFiles: (s: string) => Effect<Readdir | All, readonly string[]>;
21
21
  export declare const loadModuleMap: (io: Io) => Promise<ModuleMap>;
22
22
  export declare const index4: NodeProgram;
@@ -5,10 +5,13 @@
5
5
  */
6
6
  import { fromIo } from "../io/module.f.js";
7
7
  import { updateVersion } from "./version/module.f.js";
8
- import { all, both, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
8
+ import { access, all, both, import_, readdir, readFile, writeFile } from "../types/effects/node/module.f.js";
9
9
  import { utf8, utf8ToString } from "../text/module.f.js";
10
10
  import { unwrap } from "../types/result/module.f.js";
11
11
  import { begin, pure } from "../types/effects/module.f.js";
12
+ import { parse as jsonParse } from "../json/module.f.js";
13
+ import { record, unknown as rttiUnknown } from "../types/rtti/module.f.js";
14
+ import { parse as rttiParse } from "../types/rtti/parse/module.f.js";
12
15
  export const todo = () => { throw 'not implemented'; };
13
16
  export const assert = (v, msg = 'assertion failed') => {
14
17
  if (!v)
@@ -22,7 +25,7 @@ export const env = ({ process: { env } }) => a => {
22
25
  typeof r.get === 'function' ? r.get() :
23
26
  r.value;
24
27
  };
25
- export const allFiles2 = (s) => {
28
+ export const allFiles = (s) => {
26
29
  const load = (p) => begin
27
30
  .step(() => readdir(p, {}))
28
31
  .step(d => {
@@ -49,28 +52,34 @@ export const allFiles2 = (s) => {
49
52
  .step(v => pure(v.flat()));
50
53
  return load(s);
51
54
  };
52
- const allFiles = (io) => (s) => fromIo(io)(allFiles2(s));
55
+ const loadFile = (f) => {
56
+ const doImport = import_(f).step(r => pure([[f, unwrap(r)]]));
57
+ if (f.endsWith('.f.js')) {
58
+ return doImport;
59
+ }
60
+ if (f.endsWith('.f.ts')) {
61
+ return access(f.substring(0, f.length - 3) + '.js')
62
+ .step(r => r[0] === 'ok' ? pure([]) : doImport);
63
+ }
64
+ return pure([]);
65
+ };
53
66
  export const loadModuleMap = async (io) => {
54
- const { fs: { existsSync }, asyncImport } = io;
55
- let map = [];
56
- const initCwd = env(io)('INIT_CWD');
67
+ const { process: { env } } = io;
68
+ const initCwd = env['INIT_CWD'];
57
69
  const s = initCwd === undefined ? '.' : `${initCwd.replaceAll('\\', '/')}`;
58
- for (const f of await allFiles(io)(s)) {
59
- if (f.endsWith('.f.js') ||
60
- (f.endsWith('.f.ts') && !existsSync(f.substring(0, f.length - 3) + '.js'))) {
61
- const source = await asyncImport(f);
62
- map = [...map, [f, source]];
63
- }
64
- }
65
- return Object.fromEntries(map.toSorted(cmp));
70
+ const effect = allFiles(s)
71
+ .step(files => all(...files.map(loadFile)))
72
+ .step(entries => pure(Object.fromEntries(entries.flat().toSorted(cmp))));
73
+ return fromIo(io)(effect);
66
74
  };
67
75
  const denoJson = './deno.json';
76
+ const parseDenoJson = rttiParse(record(rttiUnknown));
68
77
  const index2 = begin
69
78
  .step(() => updateVersion)
70
79
  .step(() => readFile(denoJson))
71
- .step(v => pure(JSON.parse(utf8ToString(unwrap(v)))));
80
+ .step(v => pure(unwrap(parseDenoJson(jsonParse(utf8ToString(unwrap(v)))))));
72
81
  const allFiles2aa = begin
73
- .step(() => allFiles2('.'))
82
+ .step(() => allFiles('.'))
74
83
  .step(files => {
75
84
  const list = files.filter(v => v.endsWith('/module.f.ts') || v.endsWith('/module.ts'));
76
85
  const exportsA = list.map(v => [v, `./${v.substring(2)}`]);
@@ -9,7 +9,9 @@ import { main as testMain } from "../dev/tf/module.f.js";
9
9
  import { main as casMain } from "../cas/module.f.js";
10
10
  export const main = async (io) => {
11
11
  const { error } = io.console;
12
- const [command, ...rest] = io.process.argv.slice(2);
12
+ const { process, asyncImport } = io;
13
+ const { env } = process;
14
+ const [command, ...rest] = process.argv.slice(2);
13
15
  const eRun = fromIo(io);
14
16
  switch (command) {
15
17
  case 'test':
@@ -24,8 +26,8 @@ export const main = async (io) => {
24
26
  case 'run':
25
27
  case 'r':
26
28
  const [file, ...args] = rest;
27
- const m = await io.asyncImport(file);
28
- return eRun(m.default(args));
29
+ const m = await asyncImport(file);
30
+ return eRun(m.default(args, env));
29
31
  case undefined:
30
32
  error('Error: command is required');
31
33
  return Promise.resolve(1);
package/fs/io/module.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type Io, type Module, type Run } from './module.f.ts';
2
- import type { NodeProgram } from '../types/effects/node/module.f.ts';
1
+ import { type Io, type Run } from './module.f.ts';
2
+ import type { Module, NodeProgram } from '../types/effects/node/module.f.ts';
3
3
  export declare const asyncImport: (v: string) => Promise<Module>;
4
4
  export declare const io: Io;
5
5
  export declare const legacyRun: Run;
@@ -1,5 +1,5 @@
1
1
  import { type Effect } from '../types/effects/module.f.ts';
2
- import type { Headers, NodeOp } from '../types/effects/node/module.f.ts';
2
+ import type { Headers, Module, NodeOp, Env } 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
@@ -31,7 +31,6 @@ export type Fs = {
31
31
  readonly writeSync: (fd: number, s: string) => void;
32
32
  readonly writeFileSync: (file: string, data: Uint8Array) => void;
33
33
  readonly readFileSync: (path: string) => Uint8Array | null;
34
- readonly existsSync: (path: string) => boolean;
35
34
  readonly promises: {
36
35
  readonly readFile: (path: string) => Promise<Uint8Array>;
37
36
  readonly writeFile: (path: string, data: Uint8Array) => Promise<void>;
@@ -39,6 +38,7 @@ export type Fs = {
39
38
  readonly rm: (path: string, options?: RmOptions) => Promise<void>;
40
39
  readonly mkdir: (path: string, options?: MakeDirectoryOptions) => Promise<string | undefined>;
41
40
  readonly copyFile: (src: string, dest: string) => Promise<void>;
41
+ readonly access: (path: string) => Promise<void>;
42
42
  };
43
43
  };
44
44
  /**
@@ -49,12 +49,6 @@ export type Console = {
49
49
  readonly log: (...d: unknown[]) => void;
50
50
  readonly error: (...d: unknown[]) => void;
51
51
  };
52
- /**
53
- * Represents an ES module with a default export
54
- */
55
- export type Module = {
56
- readonly default: unknown;
57
- };
58
52
  /**
59
53
  * High-resolution time measurement interface
60
54
  * @see https://nodejs.org/api/perf_hooks.html#performance-now
@@ -118,12 +112,6 @@ export type Io = {
118
112
  readonly http: Http;
119
113
  readonly childProcess: ChildProcess;
120
114
  };
121
- /**
122
- * The environment variables.
123
- */
124
- export type Env = {
125
- readonly [k: string]: string | undefined;
126
- };
127
115
  export type App = (io: Io) => Promise<number>;
128
116
  export type Run = (f: App) => Promise<never>;
129
117
  /**
@@ -132,4 +120,4 @@ export type Run = (f: App) => Promise<never>;
132
120
  */
133
121
  export declare const run: (io: Io) => Run;
134
122
  export type EffectToPromise = <T>(effect: Effect<NodeOp, T>) => Promise<T>;
135
- export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm } }, fetch, http: { createServer }, childProcess, }: Io) => EffectToPromise;
123
+ export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm, access } }, fetch, http: { createServer }, childProcess, asyncImport, }: Io) => EffectToPromise;
package/fs/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, }) => {
38
+ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile, rm, access } }, fetch, http: { createServer }, childProcess, asyncImport, }) => {
39
39
  const result = asyncRun({
40
40
  all: async (...effects) => await Promise.all(effects.map(result)),
41
41
  error: async (message) => error(message),
@@ -50,9 +50,15 @@ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readF
50
50
  mkdir: (...p) => tc(async () => { await mkdir(...p); }),
51
51
  readFile: path => tc(async () => toVec(await readFile(path))),
52
52
  readdir: (path, r) => tc(async () => (await readdir(path, { ...r, withFileTypes: true }))
53
- .map(v => ({ name: v.name, parentPath: normalize(v.parentPath), isFile: v.isFile() }))),
53
+ .map(v => ({
54
+ name: v.name,
55
+ parentPath: normalize(v.parentPath),
56
+ isFile: v.isFile()
57
+ }))),
54
58
  writeFile: (path, data) => tc(() => writeFile(path, fromVec(data))),
55
59
  rm: path => tc(() => rm(path)),
60
+ access: path => tc(() => access(path)),
61
+ import: path => tc(() => asyncImport(path)),
56
62
  exec: (command, stdin) => new Promise(resolve => {
57
63
  const child = childProcess.exec(command, (e, stdout, stderr) => resolve(e !== null ? ['error', e] : ok({ stdout, stderr })));
58
64
  child.stdin?.end(stdin);
package/fs/io/module.js CHANGED
@@ -48,8 +48,8 @@ export const io = {
48
48
  export const legacyRun = run(io);
49
49
  export const ioRun = (io) => {
50
50
  const r = fromIo(io);
51
- const { argv } = io.process;
52
- return p => r(p(argv));
51
+ const { argv, env } = io.process;
52
+ return p => r(p(argv, env));
53
53
  };
54
54
  const effectRun = ioRun(io);
55
55
  export default effectRun;
@@ -6,10 +6,9 @@ export const createVirtualIo = (files) => ({
6
6
  error: (..._d) => { }
7
7
  },
8
8
  fs: {
9
- writeSync: (fd, s) => { },
9
+ writeSync: (_fd, _s) => { },
10
10
  writeFileSync: (_file, _data) => { },
11
11
  readFileSync: (path) => { return at(path)(files); },
12
- existsSync: (path) => { return at(path)(files) !== null; },
13
12
  promises: {
14
13
  readdir: (_path) => Promise.resolve([]),
15
14
  readFile: (_path) => Promise.resolve(new Uint8Array()),
@@ -17,6 +16,7 @@ export const createVirtualIo = (files) => ({
17
16
  rm: (_path, _options) => Promise.resolve(),
18
17
  mkdir: (_path, _options) => Promise.resolve(undefined),
19
18
  copyFile: (_src, _dest) => Promise.resolve(),
19
+ access: (_path) => Promise.resolve(),
20
20
  }
21
21
  },
22
22
  process: {
@@ -46,7 +46,9 @@ export type ExecResult = {
46
46
  };
47
47
  export type Exec = readonly ['exec', (command: string, stdin?: string) => IoResult<ExecResult>];
48
48
  export declare const exec: Func<Exec>;
49
- export type Fs = Mkdir | ReadFile | Readdir | WriteFile | Rm | Exec;
49
+ export type Access = readonly ['access', (path: string) => IoResult<void>];
50
+ export declare const access: Func<Access>;
51
+ export type Fs = Mkdir | ReadFile | Readdir | WriteFile | Rm | Exec | Access;
50
52
  export type Error = ['error', (message: string) => void];
51
53
  export declare const error: Func<Error>;
52
54
  export type Log = ['log', (message: string) => void];
@@ -75,7 +77,18 @@ export declare const listen: Func<Listen>;
75
77
  export type Http = CreateServer | Listen;
76
78
  export type Forever = ['forever', () => never];
77
79
  export declare const forever: Func<Forever>;
78
- export type NodeOp = All | Fetch | Console | Fs | Http | Forever;
80
+ export type Module = {
81
+ readonly default: unknown;
82
+ };
83
+ export type Import = ['import', (path: string) => IoResult<Module>];
84
+ export declare const import_: Func<Import>;
85
+ export type NodeOp = All | Fetch | Console | Fs | Http | Forever | Import;
79
86
  export type NodeEffect<T> = Effect<NodeOp, T>;
80
87
  export type NodeOperationMap = ToAsyncOperationMap<NodeOp>;
81
- export type NodeProgram = (argv: readonly string[]) => Effect<NodeOp, number>;
88
+ /**
89
+ * The environment variables.
90
+ */
91
+ export type Env = {
92
+ readonly [k: string]: string | undefined;
93
+ };
94
+ export type NodeProgram = (argv: readonly string[], env: Env) => Effect<NodeOp, number>;
@@ -16,8 +16,10 @@ export const readdir = do_('readdir');
16
16
  export const writeFile = do_('writeFile');
17
17
  export const rm = do_('rm');
18
18
  export const exec = do_('exec');
19
+ export const access = do_('access');
19
20
  export const error = do_('error');
20
21
  export const log = do_('log');
21
22
  export const createServer = do_('createServer');
22
23
  export const listen = do_('listen');
23
24
  export const forever = do_('forever');
25
+ export const import_ = do_('import');
@@ -94,6 +94,15 @@ const readdir = (base, recursive) => readOperation((dir, path) => {
94
94
  };
95
95
  return ok(f(base, dir));
96
96
  });
97
+ const access = readOperation((dir, path) => {
98
+ if (path.length === 0) {
99
+ return okVoid;
100
+ }
101
+ if (path.length !== 1) {
102
+ return error('no such file or directory');
103
+ }
104
+ return dir[path[0]] !== undefined ? okVoid : error('no such file or directory');
105
+ });
97
106
  const rm = operation((dir, path) => {
98
107
  if (path.length !== 1) {
99
108
  return [dir, error('invalid path')];
@@ -130,6 +139,8 @@ const map = {
130
139
  readFile,
131
140
  readdir: (state, path, { recursive }) => readdir(path, recursive === true)(state, path),
132
141
  writeFile: (state, path, payload) => writeFile(payload)(state, path),
142
+ access,
143
+ import: todo,
133
144
  rm,
134
145
  exec: todo,
135
146
  createServer: todo,
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Shared kernel for RTTI consumers (`validate`, `parse`).
3
+ *
4
+ * Both consumers traverse the same schema shape and produce the same
5
+ * `Result<T, ValidationError>` outcome. Only the per-variant handling differs
6
+ * — `validate` keeps the original value, `parse` constructs a fresh one.
7
+ *
8
+ * This module hosts the parts that do not differ:
9
+ *
10
+ * - The error shape (`ValidationError`, `Path`) and path bookkeeping
11
+ * (`verror`, `prependPath`).
12
+ * - Primitive checks (`primitive0Validate`, `constPrimitiveValidate`).
13
+ * - The `Validate<T>`/`Result<T>` signatures — `parse` uses the same shape.
14
+ * - `visit`: a visitor over the `Type` ADT. Callers supply a `Visitor<R>`
15
+ * with one handler per variant; `visit(v)(rtti)` recognizes `rtti` and
16
+ * calls the matching handler. Both consumers compose their top-level
17
+ * function from a visitor.
18
+ *
19
+ * Keeping the kernel here also removes `parse`'s incidental dependency on
20
+ * `validate` and gives future schema-driven consumers (e.g. the data form
21
+ * sketched in [i143](../../../../issues/README.md)) a stable shared base.
22
+ *
23
+ * @module
24
+ */
25
+ import type { Primitive, Unknown } from '../../../djs/module.f.ts';
26
+ import { type Info0, type Primitive0, type Struct, type Tuple, type Type } from '../module.f.ts';
27
+ import { type Error, type Result as CommonResult } from '../../result/module.f.ts';
28
+ import type { Ts } from '../ts/module.f.ts';
29
+ /** A path to a sub-value within the validated structure. Each step is an object key or stringified array index. */
30
+ export type Path = readonly string[];
31
+ /** Detailed validation failure: the offending `path` plus a short `message`. */
32
+ export type ValidationError = {
33
+ readonly path: Path;
34
+ readonly message: string;
35
+ };
36
+ /** Validation/parse result: either the typed value or a `ValidationError`. */
37
+ export type Result<T extends Type> = CommonResult<Ts<T>, ValidationError>;
38
+ /** A function that checks an unknown value against schema `T`. Shared by `validate` and `parse`. */
39
+ export type Validate<T extends Type> = (value: Unknown) => Result<T>;
40
+ /** Builds an error result with empty path and the given message. */
41
+ export declare const verror: (message: string) => Error<ValidationError>;
42
+ /** Prepends `key` to the error's path, used to build the path bottom-up. */
43
+ export declare const prependPath: (key: string, r: Error<ValidationError>) => Error<ValidationError>;
44
+ /** Validates a `Tag0` primitive schema using `typeof`. */
45
+ export declare const primitive0Validate: <K extends Primitive0, T extends Info0<K>>(tag: K) => Validate<T>;
46
+ /**
47
+ * Validates a primitive `Const` schema using `Object.is` (SameValue).
48
+ *
49
+ * `Object.is` is used instead of `===` so that:
50
+ * - `NaN` const schemas match `NaN` values (`===` would always fail because `NaN !== NaN`).
51
+ * - `+0` and `-0` are treated as distinct const values.
52
+ */
53
+ export declare const constPrimitiveValidate: <T extends Primitive>(rtti: T) => Validate<T>;
54
+ /**
55
+ * One handler per `Type` variant. Both `validate` and `parse` provide a
56
+ * `Visitor<R>` where `R` is the per-variant output (e.g. a `Validate` function).
57
+ */
58
+ export type Visitor<R> = {
59
+ readonly tuple: (rtti: Tuple) => R;
60
+ readonly struct: (rtti: Struct) => R;
61
+ readonly array: (item: Type) => R;
62
+ readonly record: (item: Type) => R;
63
+ readonly or: (variants: readonly Type[]) => R;
64
+ readonly constPrimitive: (p: Primitive) => R;
65
+ readonly primitive0: (tag: Primitive0) => R;
66
+ readonly unknown: () => R;
67
+ };
68
+ /**
69
+ * Visits a schema `Type` by dispatching to the matching handler in `v`.
70
+ *
71
+ * - `Thunk` schemas are evaluated once to read the `Info` descriptor, then
72
+ * routed by tag (`'const'`, `'array'`, `'record'`, `'unknown'`, `'or'`,
73
+ * or a `Tag0` primitive).
74
+ * - `Const` schemas (primitives, tuples, structs) are routed directly to
75
+ * `tuple`, `struct`, or `constPrimitive`.
76
+ */
77
+ export declare const visit: <R>(v: Visitor<R>) => (rtti: Type) => R;
@@ -0,0 +1,45 @@
1
+ import {} from "../module.f.js";
2
+ import { error, ok } from "../../result/module.f.js";
3
+ import { isArray } from "../../array/module.f.js";
4
+ /** Builds an error result with empty path and the given message. */
5
+ export const verror = (message) => error({ path: [], message });
6
+ /** Prepends `key` to the error's path, used to build the path bottom-up. */
7
+ export const prependPath = (key, r) => error({ path: [key, ...r[1].path], message: r[1].message });
8
+ /** Validates a `Tag0` primitive schema using `typeof`. */
9
+ export const primitive0Validate = (tag) => value => typeof value === tag ? ok(value) : verror('unexpected value');
10
+ /**
11
+ * Validates a primitive `Const` schema using `Object.is` (SameValue).
12
+ *
13
+ * `Object.is` is used instead of `===` so that:
14
+ * - `NaN` const schemas match `NaN` values (`===` would always fail because `NaN !== NaN`).
15
+ * - `+0` and `-0` are treated as distinct const values.
16
+ */
17
+ export const constPrimitiveValidate = (rtti) => value => Object.is(rtti, value)
18
+ ? ok(value)
19
+ : verror('unexpected value');
20
+ const visitConst = (v) => (c) => typeof c === 'object' && c !== null
21
+ ? (isArray(c) ? v.tuple(c) : v.struct(c))
22
+ : v.constPrimitive(c);
23
+ /**
24
+ * Visits a schema `Type` by dispatching to the matching handler in `v`.
25
+ *
26
+ * - `Thunk` schemas are evaluated once to read the `Info` descriptor, then
27
+ * routed by tag (`'const'`, `'array'`, `'record'`, `'unknown'`, `'or'`,
28
+ * or a `Tag0` primitive).
29
+ * - `Const` schemas (primitives, tuples, structs) are routed directly to
30
+ * `tuple`, `struct`, or `constPrimitive`.
31
+ */
32
+ export const visit = (v) => (rtti) => {
33
+ if (typeof rtti === 'function') {
34
+ const [tag, ...value] = rtti();
35
+ switch (tag) {
36
+ case 'const': return visitConst(v)(value[0]);
37
+ case 'array': return v.array(value[0]);
38
+ case 'record': return v.record(value[0]);
39
+ case 'unknown': return v.unknown();
40
+ case 'or': return v.or(value);
41
+ }
42
+ return v.primitive0(tag);
43
+ }
44
+ return visitConst(v)(rtti);
45
+ };
@@ -1,32 +1,8 @@
1
1
  import { type Type } from '../module.f.ts';
2
- import { type Result as ValidateResult, type Validate } from '../validate/module.f.ts';
3
- export type { Path, ValidationError } from '../validate/module.f.ts';
2
+ import { type Result as CommonValidateResult, type Validate } from '../common/module.f.ts';
3
+ export { type Path, type ValidationError } from '../common/module.f.ts';
4
4
  /** Parse result: either the freshly constructed typed value or a `ValidationError`. */
5
- export type Result<T extends Type> = ValidateResult<T>;
5
+ export type Result<T extends Type> = CommonValidateResult<T>;
6
6
  /** A function that parses an unknown value into the schema `T`. */
7
7
  export type Parse<T extends Type> = Validate<T>;
8
- /**
9
- * Creates a parser function for the given RTTI schema.
10
- *
11
- * The returned function takes an unknown value and returns either
12
- * `['ok', newValue]` containing a freshly constructed value matching the schema,
13
- * or `['error', { path, message }]` describing the failure location.
14
- *
15
- * @param rtti - A schema `Type`: a `Thunk` for tag-based schemas, or a `Const`
16
- * (primitive literal, tuple, or struct) for exact-value schemas.
17
- * @returns A `Parse<T>` function.
18
- *
19
- * @example
20
- * ```ts
21
- * const p = parse(array(number))
22
- * p([1, 2, 3]) // ['ok', [1, 2, 3]] (a new array)
23
- * p([1, 'two']) // ['error', { path: ['1'], message: 'unexpected value' }]
24
- *
25
- * // tuples are closed: extra elements are dropped
26
- * parse([number, number] as const)([1, 2, 3]) // ['ok', [1, 2]]
27
- *
28
- * // structs drop undeclared keys
29
- * parse({ a: number } as const)({ a: 1, b: 2 }) // ['ok', { a: 1 }]
30
- * ```
31
- */
32
8
  export declare const parse: <T extends Type>(rtti: T) => Parse<T>;
@@ -1,9 +1,10 @@
1
1
  import {} from "../module.f.js";
2
2
  import { ok } from "../../result/module.f.js";
3
- import { constPrimitiveValidate, prependPath, primitive0Validate, verror, } from "../validate/module.f.js";
4
3
  import { isArray as commonIsArray } from "../../array/module.f.js";
5
4
  import { isObject as commonIsObject } from "../../object/module.f.js";
6
5
  import { find, map as listMap } from "../../list/module.f.js";
6
+ import { constPrimitiveValidate, prependPath, primitive0Validate, verror, visit, } from "../common/module.f.js";
7
+ export {} from "../common/module.f.js";
7
8
  const indexedFirstError = (results) => {
8
9
  // TODO: findIndex breaks type inference,
9
10
  // we should replace it with something else.
@@ -65,12 +66,6 @@ const structParse = (rtti) => value => {
65
66
  ? ok(Object.fromEntries(results.map(([k, r]) => [k, r[1]])))
66
67
  : prependPath(err[0], err[1]));
67
68
  };
68
- const constObjectParse = (rtti) => commonIsArray(rtti)
69
- ? tupleParse(rtti)
70
- : structParse(rtti);
71
- const constParse = (rtti) => typeof rtti === 'object' && rtti !== null
72
- ? constObjectParse(rtti)
73
- : constPrimitiveValidate(rtti);
74
69
  const findFirst = find(verror('no match'))((k) => k[0] === 'ok');
75
70
  const orParse = (rtti) => value => findFirst(listMap(t => parse(t)(value))(rtti));
76
71
  /**
@@ -97,17 +92,14 @@ const orParse = (rtti) => value => findFirst(listMap(t => parse(t)(value))(rtti)
97
92
  * parse({ a: number } as const)({ a: 1, b: 2 }) // ['ok', { a: 1 }]
98
93
  * ```
99
94
  */
100
- export const parse = (rtti) => {
101
- if (typeof rtti === 'function') {
102
- const [tag, ...value] = rtti();
103
- switch (tag) {
104
- case 'const': return constParse(value[0]);
105
- case 'array': return arrayParse(value[0]);
106
- case 'record': return recordParse(value[0]);
107
- case 'unknown': return ok;
108
- case 'or': return orParse(value);
109
- }
110
- return primitive0Validate(tag);
111
- }
112
- return constParse(rtti);
95
+ const parseVisitor = {
96
+ tuple: tupleParse,
97
+ struct: structParse,
98
+ array: arrayParse,
99
+ record: recordParse,
100
+ or: orParse,
101
+ constPrimitive: constPrimitiveValidate,
102
+ primitive0: primitive0Validate,
103
+ unknown: () => ok,
113
104
  };
105
+ export const parse = (rtti) => visit(parseVisitor)(rtti);
@@ -6,9 +6,7 @@ export type Info0Ts<T extends Tag0> = T extends 'boolean' ? boolean : T extends
6
6
  /** Maps a `Const` schema to its TypeScript type. */
7
7
  export type ConstTs<T> = T extends readonly Type[] ? TupleTs<T> : T extends {
8
8
  readonly [k in string]: Type;
9
- } ? {
10
- readonly [K in keyof T]: Ts<T[K]>;
11
- } : T;
9
+ } ? StructTs<T> : T;
12
10
  /** Maps a `Tag1` and inner type to its TypeScript type. */
13
11
  export type Info1Ts<K extends Tag1, T extends Type> = K extends 'array' ? ArrayTs<T> : K extends 'record' ? RecordTs<T> : never;
14
12
  /** Maps an array schema `T` to `readonly Ts<T>[]`. */
@@ -19,10 +17,14 @@ export type RecordTs<T extends Type> = ReadonlyRecord<string, Ts<T>>;
19
17
  export type TupleTs<T extends Tuple> = {
20
18
  readonly [K in keyof T]: Ts<T[K]>;
21
19
  };
22
- /** Maps a struct schema to a readonly object of resolved types. */
23
- export type StructTs<T extends Struct> = {
24
- readonly [K in keyof T]: Ts<T[K]>;
20
+ type OptionalFields<T extends Struct> = {
21
+ readonly [K in keyof T as undefined extends Ts<T[K]> ? K : never]?: Ts<T[K]>;
22
+ };
23
+ type RequiredFields<T extends Struct> = {
24
+ readonly [K in keyof T as undefined extends Ts<T[K]> ? never : K]: Ts<T[K]>;
25
25
  };
26
+ /** Maps a struct schema to a readonly object of resolved types, with optional fields for schemas that include `undefined`. */
27
+ export type StructTs<T extends Struct> = (keyof OptionalFields<T> extends never ? unknown : OptionalFields<T>) & (keyof RequiredFields<T> extends never ? unknown : RequiredFields<T>);
26
28
  /**
27
29
  * Converts a schema `Type` to its corresponding TypeScript type.
28
30
  *
@@ -70,3 +72,4 @@ export type Ts<T extends Type> = T extends () => infer I ? (I extends readonly [
70
72
  * ```
71
73
  */
72
74
  export declare const printer: (mut?: true) => (rtti: Type) => string;
75
+ export {};
@@ -1,80 +1,4 @@
1
- /**
2
- * Runtime validation of unknown values against RTTI schemas.
3
- *
4
- * The main entry point is `validate(rtti)`, which takes a schema `Type` and returns
5
- * a `Validate<T>` function. When called with an unknown value, it returns a `Result`
6
- * that is either `['ok', typedValue]` or `['error', { path, message }]`.
7
- *
8
- * ## Error path
9
- *
10
- * On failure, the error carries a `path` pointing at the offending sub-value:
11
- * each step is the string property name (for structs/records) or the stringified
12
- * index (for tuples/arrays). The path is empty when the failure is at the root.
13
- *
14
- * ## Dispatch strategy
15
- *
16
- * - **`Thunk`** schemas are evaluated lazily: the thunk is called once to obtain an
17
- * `Info` descriptor, then dispatched by tag:
18
- * - `'const'` — delegates to `constValidate`
19
- * - `'unknown'` — always succeeds (any DJS value is valid)
20
- * - `Tag1` (`'array'`, `'record'`) — delegates to `containerValidate`
21
- * - `Tag0` (`'boolean'`, `'number'`, `'string'`, `'bigint'`) — uses `typeof` check
22
- * - `'or'` — tries each variant; reports `'no match'` at the current location if all fail
23
- * - **`Const`** schemas (primitives, tuples, structs) validate by exact equality or
24
- * recursive field/element checking.
25
- *
26
- * ## Recursion safety
27
- *
28
- * For `array` and `record` schemas, the inner item validator is instantiated lazily —
29
- * only after confirming the container is non-empty. This prevents infinite recursion
30
- * when validating recursive schemas like `const list = () => ['array', list]`.
31
- *
32
- * @module
33
- */
34
- import type { Unknown } from '../../../djs/module.f.ts';
35
- import { type Info0, type Primitive0, type Type } from '../module.f.ts';
36
- import { type Error, type Result as CommonResult } from '../../result/module.f.ts';
37
- import type { Ts } from '../ts/module.f.ts';
38
- import type { Primitive } from '../../../djs/module.f.ts';
39
- /** A path to a sub-value within the validated structure. Each step is an object key or stringified array index. */
40
- export type Path = readonly string[];
41
- /** Detailed validation failure: the offending `path` plus a short `message`. */
42
- export type ValidationError = {
43
- readonly path: Path;
44
- readonly message: string;
45
- };
46
- /** Validation result: either the typed value or a `ValidationError`. */
47
- export type Result<T extends Type> = CommonResult<Ts<T>, ValidationError>;
48
- /** A function that validates an unknown value against schema `T`. */
49
- export type Validate<T extends Type> = (value: Unknown) => Result<T>;
50
- /** Builds an error result with empty path and the given message. */
51
- export declare const verror: (message: string) => Error<ValidationError>;
52
- /** Prepends `key` to the error's path, used to build the path bottom-up. */
53
- export declare const prependPath: (key: string, r: Error<ValidationError>) => Error<ValidationError>;
54
- /** Validates a `Tag0` primitive schema using `typeof`. */
55
- export declare const primitive0Validate: <K extends Primitive0, T extends Info0<K>>(tag: K) => Validate<T>;
56
- /**
57
- * Validates a primitive `Const` schema using `Object.is` (SameValue).
58
- *
59
- * `Object.is` is used instead of `===` so that:
60
- * - `NaN` const schemas match `NaN` values (`===` would always fail because `NaN !== NaN`).
61
- * - `+0` and `-0` are treated as distinct const values.
62
- */
63
- export declare const constPrimitiveValidate: <T extends Primitive>(rtti: T) => Validate<T>;
64
- /**
65
- * Creates a validator function for the given RTTI schema.
66
- *
67
- * @param rtti - A schema `Type`: a `Thunk` for tag-based schemas, or a `Const`
68
- * (primitive literal, tuple, or struct) for exact-value schemas.
69
- * @returns A `Validate<T>` function that checks an unknown value and returns
70
- * `['ok', value]` or `['error', { path, message }]`.
71
- *
72
- * @example
73
- * ```ts
74
- * const v = validate(array(number))
75
- * v([1, 2, 3]) // ['ok', [1, 2, 3]]
76
- * v([1, 'two']) // ['error', { path: ['1'], message: 'unexpected value' }]
77
- * v(['a']) // ['error', { path: ['0'], message: 'unexpected value' }]
78
- * ```
79
- */
1
+ import { type Type } from '../module.f.ts';
2
+ import { type Validate } from '../common/module.f.ts';
3
+ export { constPrimitiveValidate, prependPath, primitive0Validate, verror, type Path, type Result, type Validate, type ValidationError, } from '../common/module.f.ts';
80
4
  export declare const validate: <T extends Type>(rtti: T) => Validate<T>;
@@ -1,11 +1,9 @@
1
1
  import {} from "../module.f.js";
2
- import { error, ok } from "../../result/module.f.js";
2
+ import { 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
- /** Builds an error result with empty path and the given message. */
6
- export const verror = (message) => error({ path: [], message });
7
- /** Prepends `key` to the error's path, used to build the path bottom-up. */
8
- export const prependPath = (key, r) => error({ path: [key, ...r[1].path], message: r[1].message });
5
+ import { constPrimitiveValidate, prependPath, primitive0Validate, verror, visit, } from "../common/module.f.js";
6
+ export { constPrimitiveValidate, prependPath, primitive0Validate, verror, } from "../common/module.f.js";
9
7
  /**
10
8
  * Builds a validator for `array` or `record` schemas.
11
9
  * The inner item validator is instantiated lazily (only when the container is
@@ -35,8 +33,6 @@ const arrayEntries = (value) => value.map((v, i) => [String(i), v]);
35
33
  const arrayValidate = containerValidate(isArray, arrayEntries);
36
34
  const isObject = value => commonIsObject(value);
37
35
  const recordValidate = containerValidate(isObject, Object.entries);
38
- /** Validates a `Tag0` primitive schema using `typeof`. */
39
- export const primitive0Validate = (tag) => value => typeof value === tag ? ok(value) : verror('unexpected value');
40
36
  /**
41
37
  * Builds a validator for `Tuple` or `Struct` const schemas.
42
38
  * Iterates over the schema's entries and validates each corresponding
@@ -57,22 +53,6 @@ const constContainerValidate = (isContainer, getItem) => (rtti) => value => {
57
53
  };
58
54
  const tupleValidate = constContainerValidate(isArray, (value, k) => value[Number(k)]);
59
55
  const structValidate = constContainerValidate(isObject, (value, k) => value[k]);
60
- const constObjectValidate = (rtti) => commonIsArray(rtti)
61
- ? tupleValidate(rtti)
62
- : structValidate(rtti);
63
- /**
64
- * Validates a primitive `Const` schema using `Object.is` (SameValue).
65
- *
66
- * `Object.is` is used instead of `===` so that:
67
- * - `NaN` const schemas match `NaN` values (`===` would always fail because `NaN !== NaN`).
68
- * - `+0` and `-0` are treated as distinct const values.
69
- */
70
- export const constPrimitiveValidate = (rtti) => value => Object.is(rtti, value)
71
- ? ok(value)
72
- : verror('unexpected value');
73
- const constValidate = (rtti) => typeof rtti === 'object' && rtti !== null
74
- ? constObjectValidate(rtti)
75
- : constPrimitiveValidate(rtti);
76
56
  const orValidate = (rtti) => {
77
57
  const all = rtti.map(r => validate(r));
78
58
  return value => {
@@ -101,17 +81,14 @@ const orValidate = (rtti) => {
101
81
  * v(['a']) // ['error', { path: ['0'], message: 'unexpected value' }]
102
82
  * ```
103
83
  */
104
- export const validate = (rtti) => {
105
- if (typeof rtti === 'function') {
106
- const [tag, ...value] = rtti();
107
- switch (tag) {
108
- case 'const': return constValidate(value[0]);
109
- case 'array': return arrayValidate(value[0]);
110
- case 'record': return recordValidate(value[0]);
111
- case 'unknown': return ok;
112
- case 'or': return orValidate(value);
113
- }
114
- return primitive0Validate(tag);
115
- }
116
- return constValidate(rtti);
84
+ const validateVisitor = {
85
+ tuple: tupleValidate,
86
+ struct: structValidate,
87
+ array: arrayValidate,
88
+ record: recordValidate,
89
+ or: orValidate,
90
+ constPrimitive: constPrimitiveValidate,
91
+ primitive0: primitive0Validate,
92
+ unknown: () => ok,
117
93
  };
94
+ export const validate = (rtti) => visit(validateVisitor)(rtti);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",