@loydjs/runtime 0.0.0 → 1.1.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.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ <div align="center">
2
+
3
+ <h1>@loydjs/runtime</h1>
4
+
5
+ <p><strong>Zero-copy execution engine for Loyd.</strong><br/>
6
+ createExecutor · zeroCopy · abortEarly · deepFreeze · strict mode.</p>
7
+
8
+ [![CI](https://github.com/b3nito404/loyd/actions/workflows/ci.yml/badge.svg)](https://github.com/b3nito404/loyd/actions/workflows/ci.yml)
9
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
10
+ [![Bundle](https://img.shields.io/badge/bundle-~2kb-brightgreen.svg)](https://bundlephobia.com/package/@loydjs/runtime)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.4%2B-blue.svg)](https://www.typescriptlang.org)
12
+ [![npm downloads](https://img.shields.io/npm/dm/@loydjs/runtime?color=6366f1&label=downloads)](https://www.npmjs.com/package/@loydjs/runtime)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## Overview
19
+
20
+ `@loydjs/runtime` provides a high-performance execution layer on top of `@loydjs/compiler`. Instead of calling `compile(schema)` and managing options manually, you create an `Executor` once with your desired options and reuse it across your entire application.
21
+
22
+ The key feature is **zero-copy mode** — on the success path, no result object is allocated. The input is returned directly, eliminating the `{ success, data, issues }` allocation that every other validation library performs on every call.
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ ```sh
29
+ npm install @loydjs/runtime
30
+ ```
31
+
32
+ > **Requires** `@loydjs/core` · `@loydjs/compiler` · Node.js ≥ 20 · TypeScript ≥ 5.4
33
+
34
+ ---
35
+
36
+ ## API
37
+
38
+ ### `createExecutor(options?)`
39
+
40
+ Creates a reusable executor with fixed options. More efficient than passing options on every call — options are resolved once at creation time.
41
+
42
+ ```ts
43
+ import { createExecutor } from "@loydjs/runtime";
44
+
45
+ const executor = createExecutor({
46
+ zeroCopy: true, // skip { success, data, issues } allocation on success
47
+ abortEarly: true, // stop validating after first error per object
48
+ freeze: true, // deep-freeze validated output
49
+ mode: "strict", // "strip" | "strict" | "passthrough"
50
+ });
51
+
52
+ const result = executor.run(UserSchema, req.body);
53
+
54
+ if (result.success) {
55
+ console.log(result.data); // typed as User, deep-frozen
56
+ }
57
+ ```
58
+
59
+ ### Pre-built executors
60
+
61
+ Three ready-to-use executors for the most common cases:
62
+
63
+ ```ts
64
+ import {
65
+ defaultExecutor, // mode: "strip", no freeze, no zeroCopy
66
+ zeroCopyExecutor, // mode: "strip", zeroCopy: true
67
+ strictExecutor, // mode: "strict", rejects unknown keys
68
+ } from "@loydjs/runtime";
69
+
70
+ // Fastest possible — no result object allocated on success
71
+ const result = zeroCopyExecutor.run(UserSchema, input);
72
+
73
+ // Reject unknown keys
74
+ const result = strictExecutor.run(UserSchema, input);
75
+ ```
76
+
77
+ ### `executor.runOrThrow(schema, input)`
78
+
79
+ Throws a descriptive `Error` on failure instead of returning a result object.
80
+
81
+ ```ts
82
+ const executor = createExecutor({ mode: "strict" });
83
+
84
+ try {
85
+ const user = executor.runOrThrow(UserSchema, req.body);
86
+ // user is typed as User
87
+ } catch (err) {
88
+ console.error(err.message);
89
+ // "Validation failed: ERR_STRING_INVALID_EMAIL at ["email"]"
90
+ }
91
+ ```
92
+
93
+ ### `deepFreeze(value)`
94
+
95
+ Recursively freezes an object. Skips already-frozen objects for performance.
96
+
97
+ ```ts
98
+ import { deepFreeze, isDeepFrozen } from "@loydjs/runtime";
99
+
100
+ const frozen = deepFreeze({ name: "Alice", address: { city: "Paris" } });
101
+ isDeepFrozen(frozen); // true
102
+ frozen.name = "Bob"; // throws in strict mode
103
+ ```
104
+
105
+ ### `getModeConfig(mode)`
106
+
107
+ Returns the configuration object for a given mode.
108
+
109
+ ```ts
110
+ import { getModeConfig } from "@loydjs/runtime";
111
+
112
+ getModeConfig("strict");
113
+ // { stripUnknownKeys: false, errorOnUnknownKeys: true, passthroughUnknownKeys: false }
114
+
115
+ getModeConfig("passthrough");
116
+ // { stripUnknownKeys: false, errorOnUnknownKeys: false, passthroughUnknownKeys: true }
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Executor options
122
+
123
+ | Option | Type | Default | Description |
124
+ |:---|:---|:---|:---|
125
+ | `mode` | `"strip" \| "strict" \| "passthrough"` | `"strip"` | How unknown keys are handled |
126
+ | `zeroCopy` | `boolean` | `false` | Skip result allocation on success |
127
+ | `abortEarly` | `boolean` | `false` | Stop at first validation error |
128
+ | `freeze` | `boolean` | `false` | Deep-freeze validated output |
129
+
130
+ ---
131
+
132
+ ## Mode comparison
133
+
134
+ | Mode | Unknown keys | Use case |
135
+ |:---|:---|:---|
136
+ | `strip` | Removed silently | APIs, form validation |
137
+ | `strict` | Error | Internal services, strict contracts |
138
+ | `passthrough` | Kept as-is | Proxies, partial validation |
139
+
140
+ ---
141
+
142
+ ## Dependencies
143
+
144
+ | Package | Role |
145
+ |:---|:---|
146
+ | `@loydjs/core` | `LoydSchema`, `LoydResult` types |
147
+ | `@loydjs/compiler` | `compile()` for JIT validation |
148
+
149
+ ---
150
+
151
+ ## Documentation
152
+
153
+ **[loyddev-psi.vercel.app](https://loyddev-psi.vercel.app)**
154
+
155
+ ---
156
+
157
+ ## License
158
+
159
+ MIT © [b3nito404](https://github.com/b3nito404)
package/dist/index.cjs CHANGED
@@ -3,6 +3,10 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
6
10
  var __copyProps = (to, from, except, desc) => {
7
11
  if (from && typeof from === "object" || typeof from === "function") {
8
12
  for (let key of __getOwnPropNames(from))
@@ -15,5 +19,121 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
15
19
 
16
20
  // src/index.ts
17
21
  var index_exports = {};
22
+ __export(index_exports, {
23
+ createExecutor: () => createExecutor,
24
+ deepFreeze: () => deepFreeze,
25
+ defaultExecutor: () => defaultExecutor,
26
+ getModeConfig: () => getModeConfig,
27
+ isDeepFrozen: () => isDeepFrozen
28
+ });
18
29
  module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/executor.ts
32
+ var import_compiler = require("@loydjs/compiler");
33
+ var _EMPTY_ISSUES = [];
34
+ function makeSuccessResult(data) {
35
+ return { success: true, data, issues: _EMPTY_ISSUES };
36
+ }
37
+ function createExecutor(options = {}) {
38
+ const resolved = {
39
+ mode: options.mode ?? "strip",
40
+ freeze: options.freeze ?? false,
41
+ zeroCopy: options.zeroCopy ?? false,
42
+ abortEarly: options.abortEarly ?? false
43
+ };
44
+ const { freeze: doFreeze, zeroCopy } = resolved;
45
+ const maybeFreeze = doFreeze ? (v) => deepFreezeImpl(v) : (v) => v;
46
+ return {
47
+ options: resolved,
48
+ run(schema, input) {
49
+ const validator = (0, import_compiler.compile)(schema);
50
+ const result = validator(input);
51
+ if (!result.success) return result;
52
+ const data = maybeFreeze(result.data);
53
+ if (zeroCopy && data === result.data) {
54
+ return result;
55
+ }
56
+ return makeSuccessResult(data);
57
+ },
58
+ runOrThrow(schema, input) {
59
+ const validator = (0, import_compiler.compile)(schema);
60
+ const result = validator(input);
61
+ if (!result.success) {
62
+ const first = result.issues[0];
63
+ throw new Error(
64
+ first?.message ?? `Validation failed: ${first?.code ?? "ERR_UNKNOWN"} at ${JSON.stringify(first?.path ?? [])}`
65
+ );
66
+ }
67
+ return maybeFreeze(result.data);
68
+ }
69
+ };
70
+ }
71
+ var defaultExecutor = createExecutor();
72
+ var zeroCopyExecutor = createExecutor({ zeroCopy: true });
73
+ var strictExecutor = createExecutor({ mode: "strict" });
74
+ function deepFreezeImpl(value) {
75
+ if (value === null || typeof value !== "object") return value;
76
+ if (Object.isFrozen(value)) return value;
77
+ Object.freeze(value);
78
+ for (const key of Object.keys(value)) {
79
+ const v = value[key];
80
+ if (v && typeof v === "object" && !Object.isFrozen(v)) {
81
+ deepFreezeImpl(v);
82
+ }
83
+ }
84
+ return value;
85
+ }
86
+
87
+ // src/mode.ts
88
+ var CONFIGS = {
89
+ strip: {
90
+ stripUnknownKeys: true,
91
+ errorOnUnknownKeys: false,
92
+ passthroughUnknownKeys: false
93
+ },
94
+ strict: {
95
+ stripUnknownKeys: false,
96
+ errorOnUnknownKeys: true,
97
+ passthroughUnknownKeys: false
98
+ },
99
+ passthrough: {
100
+ stripUnknownKeys: false,
101
+ errorOnUnknownKeys: false,
102
+ passthroughUnknownKeys: true
103
+ }
104
+ };
105
+ function getModeConfig(mode) {
106
+ return CONFIGS[mode];
107
+ }
108
+
109
+ // src/freeze.ts
110
+ function deepFreeze(value) {
111
+ if (value === null || typeof value !== "object") return value;
112
+ if (Object.isFrozen(value)) return value;
113
+ Object.freeze(value);
114
+ for (const key of Object.keys(value)) {
115
+ const v = value[key];
116
+ if (v && typeof v === "object" && !Object.isFrozen(v)) {
117
+ deepFreeze(v);
118
+ }
119
+ }
120
+ return value;
121
+ }
122
+ function isDeepFrozen(value) {
123
+ if (value === null || typeof value !== "object") return true;
124
+ if (!Object.isFrozen(value)) return false;
125
+ for (const key of Object.keys(value)) {
126
+ const v = value[key];
127
+ if (!isDeepFrozen(v)) return false;
128
+ }
129
+ return true;
130
+ }
131
+ // Annotate the CommonJS export names for ESM import in node:
132
+ 0 && (module.exports = {
133
+ createExecutor,
134
+ deepFreeze,
135
+ defaultExecutor,
136
+ getModeConfig,
137
+ isDeepFrozen
138
+ });
19
139
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type { RuntimeMode, ExecutorOptions, Executor } from \"./executor.js\";\nexport { createExecutor, defaultExecutor } from \"./executor.js\";\n\nexport type { ModeConfig } from \"./mode.js\";\nexport { getModeConfig } from \"./mode.js\";\n\nexport { deepFreeze, isDeepFrozen } from \"./freeze.js\";\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/executor.ts","../src/mode.ts","../src/freeze.ts"],"sourcesContent":["export type { RuntimeMode, ExecutorOptions, Executor } from \"./executor.js\";\nexport { createExecutor, defaultExecutor } from \"./executor.js\";\n\nexport type { ModeConfig } from \"./mode.js\";\nexport { getModeConfig } from \"./mode.js\";\n\nexport { deepFreeze, isDeepFrozen } from \"./freeze.js\";\n","import { compile } from \"@loydjs/compiler\";\nimport type { LoydResult, LoydSchema } from \"@loydjs/core\";\n\nexport type RuntimeMode = \"strict\" | \"strip\" | \"passthrough\";\n\nexport interface ExecutorOptions {\n mode?: RuntimeMode;\n /**\n * @default false\n */\n freeze?: boolean;\n /**\n * Skip creating a new result object on success return input directly.\n * @default false\n */\n zeroCopy?: boolean;\n /**\n * Stop validation after first error per object.\n * @default false\n */\n abortEarly?: boolean;\n}\n\nexport interface Executor {\n run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T>;\n runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T;\n readonly options: Required<ExecutorOptions>;\n}\n\nconst _EMPTY_ISSUES: [] = [];\n\nfunction makeSuccessResult<T>(data: T): LoydResult<T> {\n return { success: true, data, issues: _EMPTY_ISSUES };\n}\n\nexport function createExecutor(options: ExecutorOptions = {}): Executor {\n const resolved: Required<ExecutorOptions> = {\n mode: options.mode ?? \"strip\",\n freeze: options.freeze ?? false,\n zeroCopy: options.zeroCopy ?? false,\n abortEarly: options.abortEarly ?? false,\n };\n\n const { freeze: doFreeze, zeroCopy } = resolved;\n\n const maybeFreeze = doFreeze ? <T>(v: T): T => deepFreezeImpl(v) : <T>(v: T): T => v;\n\n return {\n options: resolved,\n\n run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T> {\n const validator = compile(schema);\n const result = validator(input);\n\n if (!result.success) return result;\n\n const data = maybeFreeze(result.data);\n\n if (zeroCopy && data === result.data) {\n return result;\n }\n\n return makeSuccessResult(data);\n },\n\n runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T {\n const validator = compile(schema);\n const result = validator(input);\n\n if (!result.success) {\n const first = result.issues[0];\n throw new Error(\n first?.message ??\n `Validation failed: ${first?.code ?? \"ERR_UNKNOWN\"} at ${JSON.stringify(first?.path ?? [])}`,\n );\n }\n\n return maybeFreeze(result.data);\n },\n };\n}\n\n/**strip mode*/\nexport const defaultExecutor: Executor = createExecutor();\n\nexport const zeroCopyExecutor: Executor = createExecutor({ zeroCopy: true });\n\nexport const strictExecutor: Executor = createExecutor({ mode: \"strict\" });\n\nfunction deepFreezeImpl<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n if (Object.isFrozen(value)) return value;\n\n Object.freeze(value);\n\n for (const key of Object.keys(value as object)) {\n const v = (value as Record<string, unknown>)[key];\n if (v && typeof v === \"object\" && !Object.isFrozen(v)) {\n deepFreezeImpl(v);\n }\n }\n\n return value;\n}\n","import type { RuntimeMode } from \"./executor.js\";\n\nexport interface ModeConfig {\n stripUnknownKeys: boolean;\n errorOnUnknownKeys: boolean;\n passthroughUnknownKeys: boolean;\n}\n\nconst CONFIGS: Record<RuntimeMode, ModeConfig> = {\n strip: {\n stripUnknownKeys: true,\n errorOnUnknownKeys: false,\n passthroughUnknownKeys: false,\n },\n strict: {\n stripUnknownKeys: false,\n errorOnUnknownKeys: true,\n passthroughUnknownKeys: false,\n },\n passthrough: {\n stripUnknownKeys: false,\n errorOnUnknownKeys: false,\n passthroughUnknownKeys: true,\n },\n};\n\nexport function getModeConfig(mode: RuntimeMode): ModeConfig {\n return CONFIGS[mode];\n}\n","export function deepFreeze<T>(value: T): Readonly<T> {\n if (value === null || typeof value !== \"object\") return value as Readonly<T>;\n if (Object.isFrozen(value)) return value as Readonly<T>;\n\n Object.freeze(value);\n\n for (const key of Object.keys(value as object)) {\n const v = (value as Record<string, unknown>)[key];\n if (v && typeof v === \"object\" && !Object.isFrozen(v)) {\n deepFreeze(v);\n }\n }\n\n return value as Readonly<T>;\n}\n\nexport function isDeepFrozen(value: unknown): boolean {\n if (value === null || typeof value !== \"object\") return true;\n if (!Object.isFrozen(value)) return false;\n\n for (const key of Object.keys(value as object)) {\n const v = (value as Record<string, unknown>)[key];\n if (!isDeepFrozen(v)) return false;\n }\n\n return true;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -8,24 +8,23 @@ interface ExecutorOptions {
8
8
  */
9
9
  freeze?: boolean;
10
10
  /**
11
+ * Skip creating a new result object on success return input directly.
11
12
  * @default false
12
13
  */
13
14
  zeroCopy?: boolean;
15
+ /**
16
+ * Stop validation after first error per object.
17
+ * @default false
18
+ */
19
+ abortEarly?: boolean;
14
20
  }
15
- /**
16
- * Creates an optimized executor with fixed options.
17
- * More efficient than going through the options on every call.
18
- * @example
19
- * const executor = createExecutor({ mode: "strict", freeze: true });
20
- * const result = executor.run(UserSchema, rawInput);
21
- */
22
21
  interface Executor {
23
22
  run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T>;
24
23
  runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T;
25
24
  readonly options: Required<ExecutorOptions>;
26
25
  }
27
26
  declare function createExecutor(options?: ExecutorOptions): Executor;
28
- /** Default executor (mode: "strip", freeze: false, zeroCopy: false) */
27
+ /**strip mode*/
29
28
  declare const defaultExecutor: Executor;
30
29
 
31
30
  interface ModeConfig {
package/dist/index.d.ts CHANGED
@@ -8,24 +8,23 @@ interface ExecutorOptions {
8
8
  */
9
9
  freeze?: boolean;
10
10
  /**
11
+ * Skip creating a new result object on success return input directly.
11
12
  * @default false
12
13
  */
13
14
  zeroCopy?: boolean;
15
+ /**
16
+ * Stop validation after first error per object.
17
+ * @default false
18
+ */
19
+ abortEarly?: boolean;
14
20
  }
15
- /**
16
- * Creates an optimized executor with fixed options.
17
- * More efficient than going through the options on every call.
18
- * @example
19
- * const executor = createExecutor({ mode: "strict", freeze: true });
20
- * const result = executor.run(UserSchema, rawInput);
21
- */
22
21
  interface Executor {
23
22
  run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T>;
24
23
  runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T;
25
24
  readonly options: Required<ExecutorOptions>;
26
25
  }
27
26
  declare function createExecutor(options?: ExecutorOptions): Executor;
28
- /** Default executor (mode: "strip", freeze: false, zeroCopy: false) */
27
+ /**strip mode*/
29
28
  declare const defaultExecutor: Executor;
30
29
 
31
30
  interface ModeConfig {
package/dist/index.js CHANGED
@@ -1 +1,108 @@
1
+ // src/executor.ts
2
+ import { compile } from "@loydjs/compiler";
3
+ var _EMPTY_ISSUES = [];
4
+ function makeSuccessResult(data) {
5
+ return { success: true, data, issues: _EMPTY_ISSUES };
6
+ }
7
+ function createExecutor(options = {}) {
8
+ const resolved = {
9
+ mode: options.mode ?? "strip",
10
+ freeze: options.freeze ?? false,
11
+ zeroCopy: options.zeroCopy ?? false,
12
+ abortEarly: options.abortEarly ?? false
13
+ };
14
+ const { freeze: doFreeze, zeroCopy } = resolved;
15
+ const maybeFreeze = doFreeze ? (v) => deepFreezeImpl(v) : (v) => v;
16
+ return {
17
+ options: resolved,
18
+ run(schema, input) {
19
+ const validator = compile(schema);
20
+ const result = validator(input);
21
+ if (!result.success) return result;
22
+ const data = maybeFreeze(result.data);
23
+ if (zeroCopy && data === result.data) {
24
+ return result;
25
+ }
26
+ return makeSuccessResult(data);
27
+ },
28
+ runOrThrow(schema, input) {
29
+ const validator = compile(schema);
30
+ const result = validator(input);
31
+ if (!result.success) {
32
+ const first = result.issues[0];
33
+ throw new Error(
34
+ first?.message ?? `Validation failed: ${first?.code ?? "ERR_UNKNOWN"} at ${JSON.stringify(first?.path ?? [])}`
35
+ );
36
+ }
37
+ return maybeFreeze(result.data);
38
+ }
39
+ };
40
+ }
41
+ var defaultExecutor = createExecutor();
42
+ var zeroCopyExecutor = createExecutor({ zeroCopy: true });
43
+ var strictExecutor = createExecutor({ mode: "strict" });
44
+ function deepFreezeImpl(value) {
45
+ if (value === null || typeof value !== "object") return value;
46
+ if (Object.isFrozen(value)) return value;
47
+ Object.freeze(value);
48
+ for (const key of Object.keys(value)) {
49
+ const v = value[key];
50
+ if (v && typeof v === "object" && !Object.isFrozen(v)) {
51
+ deepFreezeImpl(v);
52
+ }
53
+ }
54
+ return value;
55
+ }
56
+
57
+ // src/mode.ts
58
+ var CONFIGS = {
59
+ strip: {
60
+ stripUnknownKeys: true,
61
+ errorOnUnknownKeys: false,
62
+ passthroughUnknownKeys: false
63
+ },
64
+ strict: {
65
+ stripUnknownKeys: false,
66
+ errorOnUnknownKeys: true,
67
+ passthroughUnknownKeys: false
68
+ },
69
+ passthrough: {
70
+ stripUnknownKeys: false,
71
+ errorOnUnknownKeys: false,
72
+ passthroughUnknownKeys: true
73
+ }
74
+ };
75
+ function getModeConfig(mode) {
76
+ return CONFIGS[mode];
77
+ }
78
+
79
+ // src/freeze.ts
80
+ function deepFreeze(value) {
81
+ if (value === null || typeof value !== "object") return value;
82
+ if (Object.isFrozen(value)) return value;
83
+ Object.freeze(value);
84
+ for (const key of Object.keys(value)) {
85
+ const v = value[key];
86
+ if (v && typeof v === "object" && !Object.isFrozen(v)) {
87
+ deepFreeze(v);
88
+ }
89
+ }
90
+ return value;
91
+ }
92
+ function isDeepFrozen(value) {
93
+ if (value === null || typeof value !== "object") return true;
94
+ if (!Object.isFrozen(value)) return false;
95
+ for (const key of Object.keys(value)) {
96
+ const v = value[key];
97
+ if (!isDeepFrozen(v)) return false;
98
+ }
99
+ return true;
100
+ }
101
+ export {
102
+ createExecutor,
103
+ deepFreeze,
104
+ defaultExecutor,
105
+ getModeConfig,
106
+ isDeepFrozen
107
+ };
1
108
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/executor.ts","../src/mode.ts","../src/freeze.ts"],"sourcesContent":["import { compile } from \"@loydjs/compiler\";\nimport type { LoydResult, LoydSchema } from \"@loydjs/core\";\n\nexport type RuntimeMode = \"strict\" | \"strip\" | \"passthrough\";\n\nexport interface ExecutorOptions {\n mode?: RuntimeMode;\n /**\n * @default false\n */\n freeze?: boolean;\n /**\n * Skip creating a new result object on success return input directly.\n * @default false\n */\n zeroCopy?: boolean;\n /**\n * Stop validation after first error per object.\n * @default false\n */\n abortEarly?: boolean;\n}\n\nexport interface Executor {\n run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T>;\n runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T;\n readonly options: Required<ExecutorOptions>;\n}\n\nconst _EMPTY_ISSUES: [] = [];\n\nfunction makeSuccessResult<T>(data: T): LoydResult<T> {\n return { success: true, data, issues: _EMPTY_ISSUES };\n}\n\nexport function createExecutor(options: ExecutorOptions = {}): Executor {\n const resolved: Required<ExecutorOptions> = {\n mode: options.mode ?? \"strip\",\n freeze: options.freeze ?? false,\n zeroCopy: options.zeroCopy ?? false,\n abortEarly: options.abortEarly ?? false,\n };\n\n const { freeze: doFreeze, zeroCopy } = resolved;\n\n const maybeFreeze = doFreeze ? <T>(v: T): T => deepFreezeImpl(v) : <T>(v: T): T => v;\n\n return {\n options: resolved,\n\n run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T> {\n const validator = compile(schema);\n const result = validator(input);\n\n if (!result.success) return result;\n\n const data = maybeFreeze(result.data);\n\n if (zeroCopy && data === result.data) {\n return result;\n }\n\n return makeSuccessResult(data);\n },\n\n runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T {\n const validator = compile(schema);\n const result = validator(input);\n\n if (!result.success) {\n const first = result.issues[0];\n throw new Error(\n first?.message ??\n `Validation failed: ${first?.code ?? \"ERR_UNKNOWN\"} at ${JSON.stringify(first?.path ?? [])}`,\n );\n }\n\n return maybeFreeze(result.data);\n },\n };\n}\n\n/**strip mode*/\nexport const defaultExecutor: Executor = createExecutor();\n\nexport const zeroCopyExecutor: Executor = createExecutor({ zeroCopy: true });\n\nexport const strictExecutor: Executor = createExecutor({ mode: \"strict\" });\n\nfunction deepFreezeImpl<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n if (Object.isFrozen(value)) return value;\n\n Object.freeze(value);\n\n for (const key of Object.keys(value as object)) {\n const v = (value as Record<string, unknown>)[key];\n if (v && typeof v === \"object\" && !Object.isFrozen(v)) {\n deepFreezeImpl(v);\n }\n }\n\n return value;\n}\n","import type { RuntimeMode } from \"./executor.js\";\n\nexport interface ModeConfig {\n stripUnknownKeys: boolean;\n errorOnUnknownKeys: boolean;\n passthroughUnknownKeys: boolean;\n}\n\nconst CONFIGS: Record<RuntimeMode, ModeConfig> = {\n strip: {\n stripUnknownKeys: true,\n errorOnUnknownKeys: false,\n passthroughUnknownKeys: false,\n },\n strict: {\n stripUnknownKeys: false,\n errorOnUnknownKeys: true,\n passthroughUnknownKeys: false,\n },\n passthrough: {\n stripUnknownKeys: false,\n errorOnUnknownKeys: false,\n passthroughUnknownKeys: true,\n },\n};\n\nexport function getModeConfig(mode: RuntimeMode): ModeConfig {\n return CONFIGS[mode];\n}\n","export function deepFreeze<T>(value: T): Readonly<T> {\n if (value === null || typeof value !== \"object\") return value as Readonly<T>;\n if (Object.isFrozen(value)) return value as Readonly<T>;\n\n Object.freeze(value);\n\n for (const key of Object.keys(value as object)) {\n const v = (value as Record<string, unknown>)[key];\n if (v && typeof v === \"object\" && !Object.isFrozen(v)) {\n deepFreeze(v);\n }\n }\n\n return value as Readonly<T>;\n}\n\nexport function isDeepFrozen(value: unknown): boolean {\n if (value === null || typeof value !== \"object\") return true;\n if (!Object.isFrozen(value)) return false;\n\n for (const key of Object.keys(value as object)) {\n const v = (value as Record<string, unknown>)[key];\n if (!isDeepFrozen(v)) return false;\n }\n\n return true;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loydjs/runtime",
3
- "version": "0.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Loyd runtime — zero-copy execution engine",
5
5
  "keywords": [
6
6
  "loyd",
@@ -26,13 +26,14 @@
26
26
  }
27
27
  },
28
28
  "files": [
29
+ "README.md",
29
30
  "dist",
30
31
  "src"
31
32
  ],
32
33
  "sideEffects": false,
33
34
  "dependencies": {
34
- "@loydjs/core": "0.0.0",
35
- "@loydjs/compiler": "0.0.0"
35
+ "@loydjs/core": "1.1.0",
36
+ "@loydjs/compiler": "1.1.0"
36
37
  },
37
38
  "devDependencies": {
38
39
  "typescript": "^5.7.2",
package/src/executor.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { compile } from "@loydjs/compiler";
1
2
  import type { LoydResult, LoydSchema } from "@loydjs/core";
2
3
 
3
4
  export type RuntimeMode = "strict" | "strip" | "passthrough";
@@ -9,25 +10,95 @@ export interface ExecutorOptions {
9
10
  */
10
11
  freeze?: boolean;
11
12
  /**
13
+ * Skip creating a new result object on success return input directly.
12
14
  * @default false
13
15
  */
14
16
  zeroCopy?: boolean;
17
+ /**
18
+ * Stop validation after first error per object.
19
+ * @default false
20
+ */
21
+ abortEarly?: boolean;
15
22
  }
16
23
 
17
- /**
18
- * Creates an optimized executor with fixed options.
19
- * More efficient than going through the options on every call.
20
- * @example
21
- * const executor = createExecutor({ mode: "strict", freeze: true });
22
- * const result = executor.run(UserSchema, rawInput);
23
- */
24
24
  export interface Executor {
25
25
  run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T>;
26
26
  runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T;
27
27
  readonly options: Required<ExecutorOptions>;
28
28
  }
29
29
 
30
- export declare function createExecutor(options?: ExecutorOptions): Executor;
30
+ const _EMPTY_ISSUES: [] = [];
31
+
32
+ function makeSuccessResult<T>(data: T): LoydResult<T> {
33
+ return { success: true, data, issues: _EMPTY_ISSUES };
34
+ }
35
+
36
+ export function createExecutor(options: ExecutorOptions = {}): Executor {
37
+ const resolved: Required<ExecutorOptions> = {
38
+ mode: options.mode ?? "strip",
39
+ freeze: options.freeze ?? false,
40
+ zeroCopy: options.zeroCopy ?? false,
41
+ abortEarly: options.abortEarly ?? false,
42
+ };
43
+
44
+ const { freeze: doFreeze, zeroCopy } = resolved;
45
+
46
+ const maybeFreeze = doFreeze ? <T>(v: T): T => deepFreezeImpl(v) : <T>(v: T): T => v;
47
+
48
+ return {
49
+ options: resolved,
50
+
51
+ run<T>(schema: LoydSchema<T>, input: unknown): LoydResult<T> {
52
+ const validator = compile(schema);
53
+ const result = validator(input);
54
+
55
+ if (!result.success) return result;
56
+
57
+ const data = maybeFreeze(result.data);
58
+
59
+ if (zeroCopy && data === result.data) {
60
+ return result;
61
+ }
31
62
 
32
- /** Default executor (mode: "strip", freeze: false, zeroCopy: false) */
33
- export declare const defaultExecutor: Executor;
63
+ return makeSuccessResult(data);
64
+ },
65
+
66
+ runOrThrow<T>(schema: LoydSchema<T>, input: unknown): T {
67
+ const validator = compile(schema);
68
+ const result = validator(input);
69
+
70
+ if (!result.success) {
71
+ const first = result.issues[0];
72
+ throw new Error(
73
+ first?.message ??
74
+ `Validation failed: ${first?.code ?? "ERR_UNKNOWN"} at ${JSON.stringify(first?.path ?? [])}`,
75
+ );
76
+ }
77
+
78
+ return maybeFreeze(result.data);
79
+ },
80
+ };
81
+ }
82
+
83
+ /**strip mode*/
84
+ export const defaultExecutor: Executor = createExecutor();
85
+
86
+ export const zeroCopyExecutor: Executor = createExecutor({ zeroCopy: true });
87
+
88
+ export const strictExecutor: Executor = createExecutor({ mode: "strict" });
89
+
90
+ function deepFreezeImpl<T>(value: T): T {
91
+ if (value === null || typeof value !== "object") return value;
92
+ if (Object.isFrozen(value)) return value;
93
+
94
+ Object.freeze(value);
95
+
96
+ for (const key of Object.keys(value as object)) {
97
+ const v = (value as Record<string, unknown>)[key];
98
+ if (v && typeof v === "object" && !Object.isFrozen(v)) {
99
+ deepFreezeImpl(v);
100
+ }
101
+ }
102
+
103
+ return value;
104
+ }
package/src/freeze.ts CHANGED
@@ -1,2 +1,27 @@
1
- export declare function deepFreeze<T>(value: T): Readonly<T>;
2
- export declare function isDeepFrozen(value: unknown): boolean;
1
+ export function deepFreeze<T>(value: T): Readonly<T> {
2
+ if (value === null || typeof value !== "object") return value as Readonly<T>;
3
+ if (Object.isFrozen(value)) return value as Readonly<T>;
4
+
5
+ Object.freeze(value);
6
+
7
+ for (const key of Object.keys(value as object)) {
8
+ const v = (value as Record<string, unknown>)[key];
9
+ if (v && typeof v === "object" && !Object.isFrozen(v)) {
10
+ deepFreeze(v);
11
+ }
12
+ }
13
+
14
+ return value as Readonly<T>;
15
+ }
16
+
17
+ export function isDeepFrozen(value: unknown): boolean {
18
+ if (value === null || typeof value !== "object") return true;
19
+ if (!Object.isFrozen(value)) return false;
20
+
21
+ for (const key of Object.keys(value as object)) {
22
+ const v = (value as Record<string, unknown>)[key];
23
+ if (!isDeepFrozen(v)) return false;
24
+ }
25
+
26
+ return true;
27
+ }
package/src/mode.ts CHANGED
@@ -1,8 +1,29 @@
1
1
  import type { RuntimeMode } from "./executor.js";
2
+
2
3
  export interface ModeConfig {
3
4
  stripUnknownKeys: boolean;
4
5
  errorOnUnknownKeys: boolean;
5
6
  passthroughUnknownKeys: boolean;
6
7
  }
7
8
 
8
- export declare function getModeConfig(mode: RuntimeMode): ModeConfig;
9
+ const CONFIGS: Record<RuntimeMode, ModeConfig> = {
10
+ strip: {
11
+ stripUnknownKeys: true,
12
+ errorOnUnknownKeys: false,
13
+ passthroughUnknownKeys: false,
14
+ },
15
+ strict: {
16
+ stripUnknownKeys: false,
17
+ errorOnUnknownKeys: true,
18
+ passthroughUnknownKeys: false,
19
+ },
20
+ passthrough: {
21
+ stripUnknownKeys: false,
22
+ errorOnUnknownKeys: false,
23
+ passthroughUnknownKeys: true,
24
+ },
25
+ };
26
+
27
+ export function getModeConfig(mode: RuntimeMode): ModeConfig {
28
+ return CONFIGS[mode];
29
+ }