@loydjs/runtime 1.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 CHANGED
@@ -1,171 +1,159 @@
1
1
  <div align="center">
2
2
 
3
- <h1>Loyd</h1>
3
+ <h1>@loydjs/runtime</h1>
4
4
 
5
-
6
- **High-performance, tree-shakable schema validation for TypeScript.**
5
+ <p><strong>Zero-copy execution engine for Loyd.</strong><br/>
6
+ createExecutor · zeroCopy · abortEarly · deepFreeze · strict mode.</p>
7
7
 
8
8
  [![CI](https://github.com/b3nito404/loyd/actions/workflows/ci.yml/badge.svg)](https://github.com/b3nito404/loyd/actions/workflows/ci.yml)
9
9
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
10
- [![Bundle](https://img.shields.io/badge/bundle-0.8kb-brightgreen.svg)](https://bundlephobia.com/package/@loydjs/schema)
10
+ [![Bundle](https://img.shields.io/badge/bundle-~2kb-brightgreen.svg)](https://bundlephobia.com/package/@loydjs/runtime)
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.4%2B-blue.svg)](https://www.typescriptlang.org)
12
- [![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/b3nito404/loyd/releases)
12
+ [![npm downloads](https://img.shields.io/npm/dm/@loydjs/runtime?color=6366f1&label=downloads)](https://www.npmjs.com/package/@loydjs/runtime)
13
13
 
14
14
  </div>
15
15
 
16
16
  ---
17
17
 
18
- ## Key features
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.
19
21
 
20
- - **0.8 kb minimal bundle** : functional `pipe()` composition enables full tree-shaking; import only what you use
21
- - **JIT compiler** : `compile(schema)` generates a pure JavaScript function via `new Function()`; subsequent calls are **2× faster** than `safeParse` on hot paths
22
- - **Structured i18n** : validators emit `{ code, path, meta }`, never locale strings; swap locales at runtime without touching your schemas
23
- - **Two-pass async pipeline** : sync rules execute first; async rules only if sync passes; multiple async rules run in parallel via `Promise.all`
24
- - **Field dependency graph** : `buildDag(schema, deps)` enables incremental revalidation; change one field -> revalidate only it and its dependents
25
- - **Native React integration** : `useForm`, `useField`, `useFieldArray` with zero external dependencies, backed by the DAG
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.
26
23
 
27
24
  ---
28
25
 
29
26
  ## Installation
30
27
 
31
28
  ```sh
32
- # Core start here
33
- npm install @loydjs/schema @loydjs/core @loydjs/types
34
-
35
- # Optional packages
36
- npm install @loydjs/async # two-pass async pipeline
37
- npm install @loydjs/compiler # JIT compilation
38
- npm install @loydjs/error-engine # structured i18n
39
- npm install @loydjs/react # React hooks (requires @loydjs/graph)
40
- npm install @loydjs/graph # field dependency DAG
41
- npm install @loydjs/zod-compat # Zod migration utilities
42
- npm install @loydjs/openapi # OpenAPI 3.1 / JSON Schema export
43
- npm install @loydjs/vite # Vite / Rollup AOT plugin
29
+ npm install @loydjs/runtime
44
30
  ```
45
- > **Requires** Node.js ≥ 20, TypeScript ≥ 5.4, and `"strict": true` in your `tsconfig.json`.
31
+
32
+ > **Requires** `@loydjs/core` · `@loydjs/compiler` · Node.js ≥ 20 · TypeScript ≥ 5.4
46
33
 
47
34
  ---
48
35
 
49
- ## Quick start
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.
50
41
 
51
42
  ```ts
52
- import { object, pipe, string, number, email, minLength } from "@loydjs/schema";
53
- import { parse, safeParse } from "@loydjs/core";
54
- import type { Infer } from "@loydjs/types";
55
-
56
- // 1. Define your schema
57
- const UserSchema = object({
58
- name: pipe(string(), minLength(2)),
59
- email: pipe(string(), email()),
60
- age: number().int().min(0),
61
- });
43
+ import { createExecutor } from "@loydjs/runtime";
62
44
 
63
- // 2. Infer the TypeScript type - zero runtime cost
64
- type User = Infer<typeof UserSchema>;
65
- // -> { name: string; email: string; age: number }
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
+ });
66
51
 
67
- // 3a. parse() throws LoydError on failure
68
- const user = parse(UserSchema, req.body);
52
+ const result = executor.run(UserSchema, req.body);
69
53
 
70
- // 3b. safeParse() never throws
71
- const result = safeParse(UserSchema, formData);
72
54
  if (result.success) {
73
- console.log(result.data.name); // typed as User
74
- } else {
75
- result.issues.forEach(issue => {
76
- console.log(issue.code); // "ERR_STRING_TOO_SHORT"
77
- console.log(issue.path); // ["name"]
78
- console.log(issue.meta); // { min: 2, actual: 1 }
79
- });
55
+ console.log(result.data); // typed as User, deep-frozen
80
56
  }
81
57
  ```
82
58
 
83
- ### React forms
84
-
85
- ```tsx
86
- import { useForm } from "@loydjs/react";
87
-
88
- function LoginForm() {
89
- const { register, handleSubmit, state } = useForm({
90
- schema: LoginSchema,
91
- defaultValues: { email: "", password: "" },
92
- mode: "onChange",
93
- });
94
-
95
- return (
96
- <form onSubmit={handleSubmit(onValid, onInvalid)}>
97
- <input {...register("email")} type="email" />
98
- <input {...register("password")} type="password" />
99
- <button type="submit" disabled={state.isSubmitting}>Sign in</button>
100
- </form>
101
- );
102
- }
103
- ```
59
+ ### Pre-built executors
104
60
 
105
- ### JIT compilation
61
+ Three ready-to-use executors for the most common cases:
106
62
 
107
63
  ```ts
108
- import { compile } from "@loydjs/compiler";
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";
109
69
 
110
- const validate = compile(UserSchema);
70
+ // Fastest possible — no result object allocated on success
71
+ const result = zeroCopyExecutor.run(UserSchema, input);
111
72
 
112
- // faster on hot paths - compiled once, cached per schema instance
113
- const result = validate(input); // LoydResult<User>
73
+ // Reject unknown keys
74
+ const result = strictExecutor.run(UserSchema, input);
114
75
  ```
115
76
 
116
- ### i18n error formatting
77
+ ### `executor.runOrThrow(schema, input)`
78
+
79
+ Throws a descriptive `Error` on failure instead of returning a result object.
117
80
 
118
81
  ```ts
119
- import { configureFormatter, fr } from "@loydjs/error-engine";
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)`
120
94
 
121
- // Call once at app startup
122
- configureFormatter("fr", fr);
95
+ Recursively freezes an object. Skips already-frozen objects for performance.
123
96
 
124
- // Issues are now formatted in French
125
- const result = safeParse(UserSchema, badInput);
126
- // result.issues[0].message -> "Minimum 2 caractères (reçu : 1)"
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
127
103
  ```
128
104
 
129
- ### Migrate from Zod
105
+ ### `getModeConfig(mode)`
106
+
107
+ Returns the configuration object for a given mode.
130
108
 
131
109
  ```ts
132
- import { fromZod, runCodemod } from "@loydjs/zod-compat";
110
+ import { getModeConfig } from "@loydjs/runtime";
133
111
 
134
- // Convert a single schema
135
- const LoydUser = fromZod(zodUserSchema);
112
+ getModeConfig("strict");
113
+ // { stripUnknownKeys: false, errorOnUnknownKeys: true, passthroughUnknownKeys: false }
136
114
 
137
- // Or run the automated codemod across your entire codebase
138
- await runCodemod("./src", { write: true, verbose: true });
115
+ getModeConfig("passthrough");
116
+ // { stripUnknownKeys: false, errorOnUnknownKeys: false, passthroughUnknownKeys: true }
139
117
  ```
140
118
 
141
119
  ---
142
120
 
143
- ## Packages
144
-
145
- | Package | Description | Size |
146
- |---|---|---|
147
- | `@loydjs/core` | `parse`, `safeParse`, `LoydError`, `BaseSchema` | 3.9 kb |
148
- | `@loydjs/schema` | All primitives, composites, modifiers, refinements | tree-shakeable |
149
- | `@loydjs/types` | `Infer<>`, `InferInput<>`, `InferOutput<>` | 0 kb runtime |
150
- | `@loydjs/async` | `parseAsync`, two-pass pipeline, `AbortSignal` | ~2 kb |
151
- | `@loydjs/compiler` | `compile()`, JIT codegen, cache management | ~4 kb |
152
- | `@loydjs/error-engine` | `createFormatter`, en/fr/es/ar locales | ~3 kb |
153
- | `@loydjs/graph` | `buildDag`, `validateIncremental`, dirty tracking | ~3 kb |
154
- | `@loydjs/react` | `useForm`, `useField`, `useFieldArray`, `FormProvider` | ~8 kb |
155
- | `@loydjs/zod-compat` | `fromZod`, `toZod`, `runCodemod` | ~5 kb |
156
- | `@loydjs/openapi` | `toOpenApi`, `toJsonSchema`, `toOpenApiComponents` | ~4 kb |
157
- | `@loydjs/vite` | `loydPlugin()` - Vite/Rollup AOT compilation | ~2 kb |
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 |
158
129
 
159
130
  ---
160
131
 
161
- ## Documentation
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 |
162
139
 
163
- For the full API reference, guides, and examples, visit the official documentation:
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
164
152
 
165
- **[https://loyddev-psi.vercel.app](https://loyddev-psi.vercel.app)**
153
+ **[loyddev-psi.vercel.app](https://loyddev-psi.vercel.app)**
166
154
 
167
155
  ---
168
156
 
169
157
  ## License
170
158
 
171
- MIT
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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,sBAAwB;AA6BxB,IAAM,gBAAoB,CAAC;AAE3B,SAAS,kBAAqB,MAAwB;AACpD,SAAO,EAAE,SAAS,MAAM,MAAM,QAAQ,cAAc;AACtD;AAEO,SAAS,eAAe,UAA2B,CAAC,GAAa;AACtE,QAAM,WAAsC;AAAA,IAC1C,MAAM,QAAQ,QAAQ;AAAA,IACtB,QAAQ,QAAQ,UAAU;AAAA,IAC1B,UAAU,QAAQ,YAAY;AAAA,IAC9B,YAAY,QAAQ,cAAc;AAAA,EACpC;AAEA,QAAM,EAAE,QAAQ,UAAU,SAAS,IAAI;AAEvC,QAAM,cAAc,WAAW,CAAI,MAAY,eAAe,CAAC,IAAI,CAAI,MAAY;AAEnF,SAAO;AAAA,IACL,SAAS;AAAA,IAET,IAAO,QAAuB,OAA+B;AAC3D,YAAM,gBAAY,yBAAQ,MAAM;AAChC,YAAM,SAAS,UAAU,KAAK;AAE9B,UAAI,CAAC,OAAO,QAAS,QAAO;AAE5B,YAAM,OAAO,YAAY,OAAO,IAAI;AAEpC,UAAI,YAAY,SAAS,OAAO,MAAM;AACpC,eAAO;AAAA,MACT;AAEA,aAAO,kBAAkB,IAAI;AAAA,IAC/B;AAAA,IAEA,WAAc,QAAuB,OAAmB;AACtD,YAAM,gBAAY,yBAAQ,MAAM;AAChC,YAAM,SAAS,UAAU,KAAK;AAE9B,UAAI,CAAC,OAAO,SAAS;AACnB,cAAM,QAAQ,OAAO,OAAO,CAAC;AAC7B,cAAM,IAAI;AAAA,UACR,OAAO,WACL,sBAAsB,OAAO,QAAQ,aAAa,OAAO,KAAK,UAAU,OAAO,QAAQ,CAAC,CAAC,CAAC;AAAA,QAC9F;AAAA,MACF;AAEA,aAAO,YAAY,OAAO,IAAI;AAAA,IAChC;AAAA,EACF;AACF;AAGO,IAAM,kBAA4B,eAAe;AAEjD,IAAM,mBAA6B,eAAe,EAAE,UAAU,KAAK,CAAC;AAEpE,IAAM,iBAA2B,eAAe,EAAE,MAAM,SAAS,CAAC;AAEzE,SAAS,eAAkB,OAAa;AACtC,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,MAAI,OAAO,SAAS,KAAK,EAAG,QAAO;AAEnC,SAAO,OAAO,KAAK;AAEnB,aAAW,OAAO,OAAO,KAAK,KAAe,GAAG;AAC9C,UAAM,IAAK,MAAkC,GAAG;AAChD,QAAI,KAAK,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,GAAG;AACrD,qBAAe,CAAC;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;AC/FA,IAAM,UAA2C;AAAA,EAC/C,OAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,wBAAwB;AAAA,EAC1B;AAAA,EACA,QAAQ;AAAA,IACN,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,wBAAwB;AAAA,EAC1B;AAAA,EACA,aAAa;AAAA,IACX,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,wBAAwB;AAAA,EAC1B;AACF;AAEO,SAAS,cAAc,MAA+B;AAC3D,SAAO,QAAQ,IAAI;AACrB;;;AC5BO,SAAS,WAAc,OAAuB;AACnD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,MAAI,OAAO,SAAS,KAAK,EAAG,QAAO;AAEnC,SAAO,OAAO,KAAK;AAEnB,aAAW,OAAO,OAAO,KAAK,KAAe,GAAG;AAC9C,UAAM,IAAK,MAAkC,GAAG;AAChD,QAAI,KAAK,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,GAAG;AACrD,iBAAW,CAAC;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,aAAa,OAAyB;AACpD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AAEpC,aAAW,OAAO,OAAO,KAAK,KAAe,GAAG;AAC9C,UAAM,IAAK,MAAkC,GAAG;AAChD,QAAI,CAAC,aAAa,CAAC,EAAG,QAAO;AAAA,EAC/B;AAEA,SAAO;AACT;","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": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Loyd runtime — zero-copy execution engine",
5
5
  "keywords": [
6
6
  "loyd",
@@ -32,8 +32,8 @@
32
32
  ],
33
33
  "sideEffects": false,
34
34
  "dependencies": {
35
- "@loydjs/core": "1.0.0",
36
- "@loydjs/compiler": "1.0.0"
35
+ "@loydjs/core": "1.1.0",
36
+ "@loydjs/compiler": "1.1.0"
37
37
  },
38
38
  "devDependencies": {
39
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
+ }