@c9up/rune 0.1.6 → 0.1.8

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.
Files changed (46) hide show
  1. package/dist/MessagesProvider.d.ts +45 -0
  2. package/dist/MessagesProvider.d.ts.map +1 -0
  3. package/dist/MessagesProvider.js +77 -0
  4. package/dist/MessagesProvider.js.map +1 -0
  5. package/dist/Schema.d.ts +912 -38
  6. package/dist/Schema.d.ts.map +1 -1
  7. package/dist/Schema.js +2841 -219
  8. package/dist/Schema.js.map +1 -1
  9. package/dist/date.d.ts +36 -0
  10. package/dist/date.d.ts.map +1 -0
  11. package/dist/date.js +275 -0
  12. package/dist/date.js.map +1 -0
  13. package/dist/errors.d.ts +42 -0
  14. package/dist/errors.d.ts.map +1 -1
  15. package/dist/errors.js +37 -0
  16. package/dist/errors.js.map +1 -1
  17. package/dist/formats.d.ts +149 -0
  18. package/dist/formats.d.ts.map +1 -0
  19. package/dist/formats.js +612 -0
  20. package/dist/formats.js.map +1 -0
  21. package/dist/index.d.ts +152 -3
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +181 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/magic.d.ts +30 -0
  26. package/dist/magic.d.ts.map +1 -0
  27. package/dist/magic.js +154 -0
  28. package/dist/magic.js.map +1 -0
  29. package/dist/types.d.ts +15 -0
  30. package/dist/types.d.ts.map +1 -0
  31. package/dist/types.js +12 -0
  32. package/dist/types.js.map +1 -0
  33. package/index.darwin-arm64.node +0 -0
  34. package/index.darwin-x64.node +0 -0
  35. package/index.linux-arm64-gnu.node +0 -0
  36. package/index.linux-x64-gnu.node +0 -0
  37. package/index.win32-x64-msvc.node +0 -0
  38. package/package.json +11 -1
  39. package/src/MessagesProvider.ts +115 -0
  40. package/src/Schema.ts +3970 -215
  41. package/src/date.ts +320 -0
  42. package/src/errors.ts +59 -0
  43. package/src/formats.ts +721 -0
  44. package/src/index.ts +266 -1
  45. package/src/magic.ts +181 -0
  46. package/src/types.ts +55 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/rune",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Rune — Validation engine for the Ream framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,6 +15,7 @@
15
15
  "src"
16
16
  ],
17
17
  "devDependencies": {
18
+ "@biomejs/biome": "^2.4.10",
18
19
  "@types/node": "^22.19.15",
19
20
  "typescript": "^6.0.2",
20
21
  "vitest": "^4.1.2"
@@ -26,6 +27,14 @@
26
27
  ".": {
27
28
  "types": "./dist/index.d.ts",
28
29
  "import": "./dist/index.js"
30
+ },
31
+ "./types": {
32
+ "types": "./dist/types.d.ts",
33
+ "import": "./dist/types.js"
34
+ },
35
+ "./errors": {
36
+ "types": "./dist/errors.d.ts",
37
+ "import": "./dist/errors.js"
29
38
  }
30
39
  },
31
40
  "publishConfig": {
@@ -36,6 +45,7 @@
36
45
  "url": "git+https://github.com/C9up/rune.git"
37
46
  },
38
47
  "scripts": {
48
+ "prebuild": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
39
49
  "build": "tsc -p tsconfig.build.json",
40
50
  "build:rust": "cargo build --release -p rune-engine-napi",
41
51
  "build:napi": "pnpm build:rust && node scripts/copy-napi.mjs",
@@ -0,0 +1,115 @@
1
+ /**
2
+ * SimpleMessagesProvider — VineJS-compatible message lookup.
3
+ *
4
+ * Resolves an error message for a `(rule, field)` pair using a `'field.rule'`
5
+ * → template map, human-readable field labels, wildcard (`*`) segments for
6
+ * array items, and mustache `{{ field }}` interpolation. Mirrors
7
+ * `@vinejs/vine`'s provider so validators authored against Vine's message
8
+ * conventions behave identically here.
9
+ */
10
+
11
+ /** Rule name → message template, e.g. `{ required: 'The {{ field }} is required' }`. */
12
+ export type ValidationMessages = Record<string, string>;
13
+ /** Field path → human label, e.g. `{ 'user.email': 'Email address' }`. */
14
+ export type ValidationFields = Record<string, string>;
15
+
16
+ /** Interpolation values handed to a message template. */
17
+ export type MessageArgs = Record<string, unknown>;
18
+
19
+ /** Narrow to a plain record so dotted-path interpolation can index it. */
20
+ function isRecord(value: unknown): value is Record<string, unknown> {
21
+ return typeof value === "object" && value !== null;
22
+ }
23
+
24
+ /**
25
+ * Contract any messages provider must satisfy — a single `getMessage` lookup.
26
+ * Keeping it an interface lets consumers swap in their own (e.g. i18n-backed)
27
+ * provider without importing the concrete class.
28
+ */
29
+ export interface MessagesProviderContract {
30
+ getMessage(
31
+ rawMessage: string,
32
+ rule: string,
33
+ field: string,
34
+ args?: MessageArgs,
35
+ ): string;
36
+ }
37
+
38
+ /**
39
+ * Replace a dotted field path's numeric segments with `*` so array items share
40
+ * a single wildcard message key — `tags.0.name` → `tags.*.name` (VineJS parity).
41
+ */
42
+ export function toWildcardPath(field: string): string {
43
+ return field
44
+ .split(".")
45
+ .map((segment) => (/^\d+$/.test(segment) ? "*" : segment))
46
+ .join(".");
47
+ }
48
+
49
+ export class SimpleMessagesProvider implements MessagesProviderContract {
50
+ readonly #messages: ValidationMessages;
51
+ readonly #fields: ValidationFields;
52
+
53
+ constructor(messages: ValidationMessages, fields?: ValidationFields) {
54
+ this.#messages = messages;
55
+ this.#fields = fields ?? {};
56
+ }
57
+
58
+ /** Mustache-style `{{ token }}` interpolation with dotted-path lookup. */
59
+ #interpolate(message: string, data: MessageArgs): string {
60
+ if (!message.includes("{{")) {
61
+ return message;
62
+ }
63
+ return message.replace(/{{(.*?)}}/g, (_match, rawKey: string) => {
64
+ const tokens = rawKey.trim().split(".");
65
+ let output: unknown = data;
66
+ for (const token of tokens) {
67
+ if (!isRecord(output)) {
68
+ return "";
69
+ }
70
+ output = Object.hasOwn(output, token) ? output[token] : undefined;
71
+ }
72
+ return output === undefined || output === null ? "" : String(output);
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Resolve a message. Priority (VineJS order):
78
+ * 1. field-specific `user.email.required`
79
+ * 2. wildcard path `*.required` / `tags.*.minLength`
80
+ * 3. generic rule `required`
81
+ * 4. raw rule message (fallback)
82
+ */
83
+ getMessage(
84
+ rawMessage: string,
85
+ rule: string,
86
+ field: string,
87
+ args?: MessageArgs,
88
+ ): string {
89
+ const fieldName = this.#fields[field] ?? field;
90
+ const data: MessageArgs = { field: fieldName, ...args };
91
+
92
+ const fieldMessage = this.#messages[`${field}.${rule}`];
93
+ if (fieldMessage !== undefined) {
94
+ return this.#interpolate(fieldMessage, data);
95
+ }
96
+
97
+ const wildcard = toWildcardPath(field);
98
+ const wildcardMessage =
99
+ wildcard !== field ? this.#messages[`${wildcard}.${rule}`] : undefined;
100
+ if (wildcardMessage !== undefined) {
101
+ return this.#interpolate(wildcardMessage, data);
102
+ }
103
+
104
+ const ruleMessage = this.#messages[rule];
105
+ if (ruleMessage !== undefined) {
106
+ return this.#interpolate(ruleMessage, data);
107
+ }
108
+
109
+ return this.#interpolate(rawMessage, data);
110
+ }
111
+
112
+ toJSON(): { messages: ValidationMessages; fields: ValidationFields } {
113
+ return { messages: this.#messages, fields: this.#fields };
114
+ }
115
+ }