functionalscript 0.12.10 → 0.13.1

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/ci/module.f.js CHANGED
@@ -69,7 +69,7 @@ const node = (version) => (extra) => basicNode(version)([
69
69
  const findTgz = (v) => v === 'windows' ? '(Get-ChildItem *.tgz).FullName' : './*.tgz';
70
70
  const playwrightVersion = '1.58.2';
71
71
  const playwrightAndVersion = `playwright@${playwrightVersion}`;
72
- const rustToolchain = '1.94.1';
72
+ const rustToolchain = '1.95.0';
73
73
  const toSteps = (m) => {
74
74
  const filter = (st) => m.flatMap((mt) => mt.type === st ? [mt.step] : []);
75
75
  const aptGet = m.flatMap(v => v.type === 'apt-get' ? [v.package] : []).join(' ');
@@ -29,5 +29,19 @@ export declare const html: (_: Element) => List<string>;
29
29
  * Renders an HTML element tree to a final string.
30
30
  */
31
31
  export declare const htmlToString: (_: Element) => string;
32
+ /**
33
+ * Renders a complete UTF-8 encoded HTML document as a `Vec`.
34
+ *
35
+ * Produces a full page with `<!DOCTYPE html>`, a `<head>` containing a UTF-8
36
+ * `<meta charset>` and a responsive-viewport `<meta>` followed by any extra
37
+ * `head` nodes, and a `<body>` containing the provided `body` nodes.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * htmlUtf8(['title', 'My Page'])(['h1', 'Hello'])
42
+ * // Vec of UTF-8 bytes for:
43
+ * // <!DOCTYPE html><html><head><meta charset="UTF-8">...<title>My Page</title></head><body><h1>Hello</h1></body></html>
44
+ * ```
45
+ */
32
46
  export declare const htmlUtf8: (...head: readonly Node[]) => (...body: readonly Node[]) => Vec;
33
47
  export {};
package/html/module.f.js CHANGED
@@ -89,5 +89,24 @@ export const html = compose(element)(listConcat(['<!DOCTYPE html>']));
89
89
  * Renders an HTML element tree to a final string.
90
90
  */
91
91
  export const htmlToString = compose(html)(stringConcat);
92
- const metaUtf8 = ['meta', { charset: 'UTF-8' }];
93
- export const htmlUtf8 = (...head) => (...body) => utf8(htmlToString(['html', ['head', metaUtf8, ...head], ['body', ...body]]));
92
+ const commonHead = [
93
+ ['meta', { charset: 'UTF-8' }],
94
+ ['meta', { name: 'viewport', content: 'width=device-width,initial-scale=1.0' }],
95
+ ];
96
+ /**
97
+ * Renders a complete UTF-8 encoded HTML document as a `Vec`.
98
+ *
99
+ * Produces a full page with `<!DOCTYPE html>`, a `<head>` containing a UTF-8
100
+ * `<meta charset>` and a responsive-viewport `<meta>` followed by any extra
101
+ * `head` nodes, and a `<body>` containing the provided `body` nodes.
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * htmlUtf8(['title', 'My Page'])(['h1', 'Hello'])
106
+ * // Vec of UTF-8 bytes for:
107
+ * // <!DOCTYPE html><html><head><meta charset="UTF-8">...<title>My Page</title></head><body><h1>Hello</h1></body></html>
108
+ * ```
109
+ */
110
+ export const htmlUtf8 = (...head) => (...body) => utf8(htmlToString(['html',
111
+ ['head', ...commonHead, ...head],
112
+ ['body', ...body]]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.12.10",
3
+ "version": "0.13.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -41,9 +41,10 @@ export type Ts<T extends Type> = T extends () => infer I ? (I extends readonly [
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
43
  /**
44
- * Converts an RTTI schema `Type` to its TypeScript type expression as a string.
44
+ * Creates a printer that converts an RTTI schema `Type` to its TypeScript type expression as a string.
45
45
  *
46
46
  * Mirrors the compile-time `Ts<T>` mapped type at runtime.
47
+ * Pass `true` to emit mutable (non-`readonly`) types.
47
48
  *
48
49
  * **Note:** recursive schemas (e.g. `const list = () => ['array', list] as const`)
49
50
  * will cause infinite recursion. Only acyclic schemas are supported.
@@ -53,14 +54,19 @@ export type Ts<T extends Type> = T extends () => infer I ? (I extends readonly [
53
54
  *
54
55
  * @example
55
56
  * ```ts
57
+ * const toTs = printer()
56
58
  * toTs(boolean) // 'boolean'
57
59
  * toTs(array(number)) // 'readonly(number)[]'
58
- * toTs(record(string)) // '{readonly[k in string]:string}'
60
+ * toTs(record(string)) // '{readonly[k:string]:string}'
59
61
  * toTs(or(string, number)) // 'string|number'
60
62
  * toTs(42) // '42'
61
63
  * toTs('hello') // '"hello"'
62
64
  * toTs([boolean, number]) // 'readonly[boolean,number]'
63
- * toTs({ x: string }) // '{readonly "x":string}'
65
+ * toTs({ x: string }) // '{readonly"x":string}'
66
+ *
67
+ * const toTsMut = printer(true)
68
+ * toTsMut(array(number)) // '(number)[]'
69
+ * toTsMut(record(string)) // '{[k:string]:string}'
64
70
  * ```
65
71
  */
66
- export declare const toTs: (rtti: Type) => string;
72
+ export declare const printer: (mut?: true) => (rtti: Type) => string;
@@ -7,15 +7,12 @@
7
7
  * The runtime `toTs` function mirrors `Ts<T>` at value level, returning a TypeScript
8
8
  * type expression string for a given RTTI schema.
9
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)]));
10
+ import { primitive, union, printer as tsPrinter } from "../../ts/module.f.js";
15
11
  /**
16
- * Converts an RTTI schema `Type` to its TypeScript type expression as a string.
12
+ * Creates a printer that converts an RTTI schema `Type` to its TypeScript type expression as a string.
17
13
  *
18
14
  * Mirrors the compile-time `Ts<T>` mapped type at runtime.
15
+ * Pass `true` to emit mutable (non-`readonly`) types.
19
16
  *
20
17
  * **Note:** recursive schemas (e.g. `const list = () => ['array', list] as const`)
21
18
  * will cause infinite recursion. Only acyclic schemas are supported.
@@ -25,26 +22,38 @@ const constToTs = (rtti) => typeof rtti !== 'object' || rtti === null ? primitiv
25
22
  *
26
23
  * @example
27
24
  * ```ts
25
+ * const toTs = printer()
28
26
  * toTs(boolean) // 'boolean'
29
27
  * toTs(array(number)) // 'readonly(number)[]'
30
- * toTs(record(string)) // '{readonly[k in string]:string}'
28
+ * toTs(record(string)) // '{readonly[k:string]:string}'
31
29
  * toTs(or(string, number)) // 'string|number'
32
30
  * toTs(42) // '42'
33
31
  * toTs('hello') // '"hello"'
34
32
  * toTs([boolean, number]) // 'readonly[boolean,number]'
35
- * toTs({ x: string }) // '{readonly "x":string}'
33
+ * toTs({ x: string }) // '{readonly"x":string}'
34
+ *
35
+ * const toTsMut = printer(true)
36
+ * toTsMut(array(number)) // '(number)[]'
37
+ * toTsMut(record(string)) // '{[k:string]:string}'
36
38
  * ```
37
39
  */
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
- }
40
+ export const printer = (mut) => {
41
+ const { tuple, struct, array, record } = tsPrinter(mut);
42
+ const constToTs = (rtti) => typeof rtti !== 'object' || rtti === null ? primitive(rtti) :
43
+ rtti instanceof Array ? tuple(rtti.map(toTs)) :
44
+ struct(Object.entries(rtti).map(([k, v]) => [k, toTs(v)]));
45
+ const toTs = (rtti) => {
46
+ if (typeof rtti !== 'function') {
47
+ return constToTs(rtti);
48
+ }
49
+ const [tag, ...rest] = rtti();
50
+ switch (tag) {
51
+ case 'const': return constToTs(rest[0]);
52
+ case 'array': return array(toTs(rest[0]));
53
+ case 'record': return record(toTs(rest[0]));
54
+ case 'or': return union(rest.map(toTs));
55
+ default: return tag; // tag0: 'boolean' | 'number' | 'string' | 'bigint' | 'unknown'
56
+ }
57
+ };
58
+ return toTs;
50
59
  };
@@ -49,5 +49,12 @@ declare const _default: {
49
49
  };
50
50
  never: () => void;
51
51
  option: () => void;
52
+ mut: {
53
+ array: () => void;
54
+ nestedArray: () => void;
55
+ record: () => void;
56
+ tuple: () => void;
57
+ struct: () => void;
58
+ };
52
59
  };
53
60
  export default _default;
@@ -1,5 +1,13 @@
1
- import { toTs } from "./module.f.js";
1
+ import { printer } from "./module.f.js";
2
2
  import { boolean, number, string, bigint, unknown, array, record, or, option, never } from "../module.f.js";
3
+ const toTs = printer();
4
+ const toTsMut = printer(true);
5
+ const eqMut = (rtti, expected) => {
6
+ const result = toTsMut(rtti);
7
+ if (result !== expected) {
8
+ throw `expected ${JSON.stringify(expected)}, got ${JSON.stringify(result)}`;
9
+ }
10
+ };
3
11
  const eq = (rtti, expected) => {
4
12
  const result = toTs(rtti);
5
13
  if (result !== expected) {
@@ -21,8 +29,8 @@ export default {
21
29
  union: () => eq(array(or(number, string)), 'readonly(number|string)[]'),
22
30
  },
23
31
  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}}'),
32
+ primitive: () => eq(record(string), '{readonly[k:string]:string}'),
33
+ nested: () => eq(record(record(number)), '{readonly[k:string]:{readonly[k:string]:number}}'),
26
34
  },
27
35
  },
28
36
  const: {
@@ -57,4 +65,11 @@ export default {
57
65
  },
58
66
  never: () => eq(never, 'never'),
59
67
  option: () => eq(option(number), 'number|undefined'),
68
+ mut: {
69
+ array: () => eqMut(array(number), '(number)[]'),
70
+ nestedArray: () => eqMut(array(array(boolean)), '((boolean)[])[]'),
71
+ record: () => eqMut(record(string), '{[k:string]:string}'),
72
+ tuple: () => eqMut([12, true], '[12,true]'),
73
+ struct: () => eqMut({ a: number, b: string }, '{"a":number,"b":string}'),
74
+ },
60
75
  };
@@ -1,8 +1,17 @@
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;
3
+ /** Functions for emitting TypeScript type expression strings. */
4
+ export type Printer = {
5
+ /** Emits a tuple type: `readonly[A, B]` or `[A, B]` when mutable. */
6
+ readonly tuple: (types: readonly string[]) => string;
7
+ /** Emits an object type with named fields: `{readonly"k":T}` or `{"k":T}` when mutable. */
8
+ readonly struct: (fields: readonly (readonly [string, string])[]) => string;
9
+ /** Emits an array type: `readonly(T)[]` or `(T)[]` when mutable. */
10
+ readonly array: (type: string) => string;
11
+ /** Emits an index-signature record type: `{readonly[k:string]:T}` or `{[k:string]:T}` when mutable. */
12
+ readonly record: (type: string) => string;
13
+ };
14
+ /** Creates a `Printer`. Pass `true` to emit mutable (non-`readonly`) types. */
15
+ export declare const printer: (mut?: true) => Printer;
7
16
  export declare const primitive: (c: bigint | string | undefined | boolean | number | null) => string;
8
17
  export declare const union: (types: readonly string[]) => string;
@@ -1,9 +1,15 @@
1
1
  const complex = (open, close) => (i) => `${open}${i.join(',')}${close}`;
2
- export const tuple = complex('readonly[', ']');
3
2
  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}`]);
3
+ /** Creates a `Printer`. Pass `true` to emit mutable (non-`readonly`) types. */
4
+ export const printer = (mut) => {
5
+ const ro = mut ? '' : 'readonly';
6
+ return {
7
+ tuple: (mut ? complex('[', ']') : complex('readonly[', ']')),
8
+ struct: (fields) => structX(fields.map(([k, v]) => `${ro}${JSON.stringify(k)}:${v}`)),
9
+ array: (type) => `${ro}(${type})[]`,
10
+ record: (type) => structX([`${ro}[k:string]:${type}`]),
11
+ };
12
+ };
7
13
  export const primitive = (c) => {
8
14
  if (c === null) {
9
15
  return 'null';
@@ -1,4 +1,4 @@
1
- import { type NodeOp } from '../types/effects/node/module.f.ts';
1
+ import { type WriteFile } from '../types/effects/node/module.f.ts';
2
2
  import { type Effect } from '../types/effects/module.f.ts';
3
- declare const _default: () => Effect<NodeOp, number>;
3
+ declare const _default: () => Effect<WriteFile, number>;
4
4
  export default _default;
@@ -3,17 +3,13 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { htmlToString } from "../html/module.f.js";
6
+ import { htmlUtf8 } from "../html/module.f.js";
7
7
  import { writeFile } from "../types/effects/node/module.f.js";
8
- import { utf8 } from "../text/module.f.js";
9
- import { begin, pure } from "../types/effects/module.f.js";
10
- const html = utf8(htmlToString(['body',
11
- ['a',
12
- { href: 'https://github.com/functionalscript/functionalscript' },
13
- 'GitHub Repository'
14
- ]
15
- ]));
16
- const program = begin
17
- .step(() => writeFile('index.html', html))
8
+ import { pure } from "../types/effects/module.f.js";
9
+ const html = htmlUtf8()(['a',
10
+ { href: 'https://github.com/functionalscript/functionalscript' },
11
+ 'GitHub Repository'
12
+ ]);
13
+ const program = writeFile('index.html', html)
18
14
  .step(() => pure(0));
19
15
  export default () => program;