usewagen 0.0.0 → 0.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.
@@ -0,0 +1,84 @@
1
+ import { _ as warnDev, h as ErrorCodes } from "../parsers-CA8aZuF-.mjs";
2
+ import { a as createLocalStorage, c as createStorage, o as createMemoryStorage, r as getActiveWagen, s as createSessionStorage } from "../wagen-C9_CPUdF.mjs";
3
+ import { i as toValueDeep, n as serializeValue, r as resolveParser, t as parseValue } from "../utils-B-885gDp.mjs";
4
+ import { computed, customRef, getCurrentScope, onWatcherCleanup, toValue, watch } from "vue";
5
+ //#region src/storage/define-storage-state.ts
6
+ function defineStorageState(options) {
7
+ const { key } = options;
8
+ const clearOnDefault = options.clearOnDefault ?? true;
9
+ const parser = resolveParser(options.parser);
10
+ function storage() {
11
+ const source = options.storage;
12
+ if (source && typeof source !== "string") return source;
13
+ const { storage } = getActiveWagen();
14
+ return source ? storage[source] : storage.default;
15
+ }
16
+ return {
17
+ key,
18
+ get storage() {
19
+ return storage();
20
+ },
21
+ get: () => parseValue(parser, storage().getItem(key)),
22
+ set: (next) => {
23
+ const serialized = serializeValue(parser, clearOnDefault, next);
24
+ if (serialized === null) storage().removeItem(key);
25
+ else storage().setItem(key, serialized);
26
+ },
27
+ remove: () => storage().removeItem(key),
28
+ subscribe: (listener) => storage().subscribe(key, listener)
29
+ };
30
+ }
31
+ //#endregion
32
+ //#region src/storage/use-storage.ts
33
+ function isStorageState(input) {
34
+ return typeof input === "object" && input !== null && typeof input.get === "function";
35
+ }
36
+ function withResolvedStorage(options, wagen) {
37
+ const source = options.storage;
38
+ if (source && typeof source !== "string") return options;
39
+ return {
40
+ ...options,
41
+ storage: source ? wagen.storage[source] : wagen.storage.default
42
+ };
43
+ }
44
+ function useStorageState(input) {
45
+ if (isStorageState(input)) return computed(() => input);
46
+ const wagen = getActiveWagen();
47
+ return computed(() => defineStorageState(withResolvedStorage(toValueDeep(input), wagen)));
48
+ }
49
+ function useStorage(input) {
50
+ if (!getCurrentScope()) warnDev(ErrorCodes.NO_EFFECT_SCOPE, "useStorage");
51
+ const state = useStorageState(input);
52
+ let notify;
53
+ const value = customRef((track, trigger) => {
54
+ notify = trigger;
55
+ return {
56
+ get: () => {
57
+ track();
58
+ return state.value.get();
59
+ },
60
+ set: (next) => state.value.set(next)
61
+ };
62
+ });
63
+ watch(state, (current) => {
64
+ onWatcherCleanup(current.subscribe(notify));
65
+ }, {
66
+ immediate: true,
67
+ flush: "sync"
68
+ });
69
+ return value;
70
+ }
71
+ function useLocalStorage(options) {
72
+ return useStorage(() => ({
73
+ ...toValue(options),
74
+ storage: "local"
75
+ }));
76
+ }
77
+ function useSessionStorage(options) {
78
+ return useStorage(() => ({
79
+ ...toValue(options),
80
+ storage: "session"
81
+ }));
82
+ }
83
+ //#endregion
84
+ export { createLocalStorage, createMemoryStorage, createSessionStorage, createStorage, defineStorageState, useLocalStorage, useSessionStorage, useStorage };
@@ -0,0 +1,60 @@
1
+ //#region src/parser/parsers.d.ts
2
+ type ParserOptions<T> = {
3
+ parse: (raw: string) => T | null;
4
+ serialize?: (value: T) => string;
5
+ };
6
+ type DefaultValue<T> = T | (() => T);
7
+ type Parser<T> = {
8
+ parse: (raw: string) => T | null;
9
+ serialize: (value: T) => string;
10
+ withDefault: (value: DefaultValue<T>) => ParserWithDefault<T>;
11
+ };
12
+ type ParserWithDefault<T> = Parser<T> & {
13
+ readonly defaultValue: DefaultValue<T>;
14
+ };
15
+ declare function tryParse<I, R>(fn: (input: I) => R, input: I): R | null;
16
+ declare function defineParser<T>(options: ParserOptions<T>): Parser<T>;
17
+ declare function unwrapDefault<T>(value: DefaultValue<T>): T;
18
+ declare const parseAsString: Parser<string>;
19
+ declare const parseAsInteger: Parser<number>;
20
+ declare const parseAsFloat: Parser<number>;
21
+ declare const parseAsIndex: Parser<number>;
22
+ declare const parseAsBoolean: Parser<boolean>;
23
+ declare function parseAsStringLiteral<const T extends readonly string[]>(values: T): Parser<T[number]>;
24
+ declare function parseAsNumberLiteral<const T extends readonly number[]>(values: T): Parser<T[number]>;
25
+ declare function parseAsStringEnum<T extends string>(values: T[]): Parser<T>;
26
+ declare const parseAsDate: Parser<Date> & {
27
+ iso: () => Parser<Date>;
28
+ timestamp: () => Parser<Date>;
29
+ };
30
+ declare function parseAsArrayOf<T>(itemParser: Parser<T>, separator?: string): Parser<T[]>;
31
+ declare function parseAsJson<T>(): Parser<T>;
32
+ //#endregion
33
+ //#region src/parser/types.d.ts
34
+ type BuiltinParsers = {
35
+ parseAsString: Parser<string>;
36
+ parseAsInteger: Parser<number>;
37
+ parseAsFloat: Parser<number>;
38
+ parseAsIndex: Parser<number>;
39
+ parseAsBoolean: Parser<boolean>;
40
+ parseAsDate: Parser<Date>;
41
+ };
42
+ interface CustomParsers {}
43
+ type KnownParsers = BuiltinParsers & Omit<CustomParsers, keyof BuiltinParsers>;
44
+ type InferParserValue<P> = P extends Parser<infer T> ? T : never;
45
+ type NamedParserRef = { [K in keyof KnownParsers]: {
46
+ name: K;
47
+ defaultValue?: DefaultValue<InferParserValue<KnownParsers[K]>>;
48
+ }; }[keyof KnownParsers];
49
+ type ParserInput = (Parser<any> & {
50
+ name?: never;
51
+ }) | NamedParserRef;
52
+ type InferInputValue<P> = [P] extends [undefined] ? string | null : P extends ParserWithDefault<infer T> ? T : P extends Parser<infer T> ? T | null : P extends {
53
+ name: infer K;
54
+ defaultValue: any;
55
+ } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> : string : P extends {
56
+ name: infer K;
57
+ } ? K extends keyof KnownParsers ? InferParserValue<KnownParsers[K]> | null : string | null : string | null;
58
+ type InferInputWritable<P> = InferInputValue<P> | null | undefined;
59
+ //#endregion
60
+ export { parseAsStringLiteral as C, parseAsStringEnum as S, unwrapDefault as T, parseAsIndex as _, InferParserValue as a, parseAsNumberLiteral as b, DefaultValue as c, ParserWithDefault as d, defineParser as f, parseAsFloat as g, parseAsDate as h, InferInputWritable as i, Parser as l, parseAsBoolean as m, CustomParsers as n, KnownParsers as o, parseAsArrayOf as p, InferInputValue as r, ParserInput as s, BuiltinParsers as t, ParserOptions as u, parseAsInteger as v, tryParse as w, parseAsString as x, parseAsJson as y };
@@ -0,0 +1,24 @@
1
+ import { c as DefaultValue, s as ParserInput } from "./types-CNt2lcP1.mjs";
2
+ //#region src/parser/resolve.d.ts
3
+ type ResolvedParser<T> = {
4
+ parse: (raw: string) => T | null;
5
+ serialize: (value: T) => string;
6
+ defaultValue?: DefaultValue<T>;
7
+ };
8
+ //#endregion
9
+ //#region src/router/types.d.ts
10
+ type RouteStateSource = 'params' | 'query';
11
+ type HistoryMode = 'push' | 'replace';
12
+ interface RouteStateOptions {
13
+ key: string;
14
+ parser?: ParserInput;
15
+ urlKey?: string;
16
+ source?: RouteStateSource;
17
+ history?: HistoryMode;
18
+ clearOnDefault?: boolean;
19
+ }
20
+ type ResolvedRouteStateOptions = Omit<Required<RouteStateOptions>, 'parser'> & {
21
+ parser: ResolvedParser<any>;
22
+ };
23
+ //#endregion
24
+ export { RouteStateSource as i, ResolvedRouteStateOptions as n, RouteStateOptions as r, HistoryMode as t };
@@ -0,0 +1,76 @@
1
+ import { g as warn, h as ErrorCodes, m as unwrapDefault, u as parseAsString } from "./parsers-CA8aZuF-.mjs";
2
+ import { r as getActiveWagen } from "./wagen-C9_CPUdF.mjs";
3
+ import { n as isBuiltinParserName, t as builtinParsers } from "./builtins-DiCjLgsG.mjs";
4
+ import { toValue } from "vue";
5
+ //#region src/options.ts
6
+ function toValueDeep(input) {
7
+ const source = toValue(input);
8
+ const result = {};
9
+ for (const key in source) result[key] = toValue(source[key]);
10
+ return result;
11
+ }
12
+ //#endregion
13
+ //#region src/parser/resolve.ts
14
+ function getParser(name) {
15
+ if (isBuiltinParserName(name)) return builtinParsers[name];
16
+ const { parsers } = getActiveWagen();
17
+ return Object.hasOwn(parsers, name) ? parsers[name] : void 0;
18
+ }
19
+ function fromParser(parser) {
20
+ const resolved = {
21
+ parse: parser.parse,
22
+ serialize: parser.serialize
23
+ };
24
+ if ("defaultValue" in parser) resolved.defaultValue = parser.defaultValue;
25
+ return resolved;
26
+ }
27
+ function fromName(name, override) {
28
+ let warned = false;
29
+ function get() {
30
+ const found = getParser(name);
31
+ if (found) return found;
32
+ if (!warned) {
33
+ warned = true;
34
+ warn(ErrorCodes.UNKNOWN_PARSER_NAME, name);
35
+ }
36
+ return parseAsString;
37
+ }
38
+ const base = {
39
+ parse: (raw) => get().parse(raw),
40
+ serialize: (value) => get().serialize(value)
41
+ };
42
+ if (override) return {
43
+ ...base,
44
+ defaultValue: override.defaultValue
45
+ };
46
+ return {
47
+ ...base,
48
+ get defaultValue() {
49
+ return get().defaultValue;
50
+ }
51
+ };
52
+ }
53
+ function resolveParser(input = { name: "parseAsString" }) {
54
+ if ("parse" in input) return fromParser(input);
55
+ const override = "defaultValue" in input ? { defaultValue: input.defaultValue } : void 0;
56
+ return fromName(input.name, override);
57
+ }
58
+ //#endregion
59
+ //#region src/parser/utils.ts
60
+ function parseValue(parser, raw) {
61
+ const value = Array.isArray(raw) ? raw.find((v) => v != null) : raw;
62
+ if (value == null) return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
63
+ const parsed = parser.parse(value);
64
+ if (parsed !== null) return parsed;
65
+ return parser.defaultValue !== void 0 ? unwrapDefault(parser.defaultValue) : null;
66
+ }
67
+ function serializeValue(parser, clearOnDefault, next) {
68
+ if (next == null) return null;
69
+ const serialized = parser.serialize(next);
70
+ if (clearOnDefault && parser.defaultValue !== void 0) {
71
+ if (serialized === parser.serialize(unwrapDefault(parser.defaultValue))) return null;
72
+ }
73
+ return serialized;
74
+ }
75
+ //#endregion
76
+ export { toValueDeep as i, serializeValue as n, resolveParser as r, parseValue as t };
@@ -0,0 +1,19 @@
1
+ import { Plugin } from "vite";
2
+ //#region src/vite/index.d.ts
3
+ declare const PARSERS_ID = "virtual:usewagen/parsers";
4
+ declare const RESOLVED_PARSERS_ID = "\0virtual:usewagen/parsers";
5
+ interface WagenPluginOptions {
6
+ /**
7
+ * Directory or directories to scan for parser files.
8
+ * @default 'src/parsers'
9
+ */
10
+ dirs?: string | string[];
11
+ /**
12
+ * Output path for the generated `.d.ts` file, or `false` to disable.
13
+ * @default 'usewagen.d.ts'
14
+ */
15
+ dts?: string | false;
16
+ }
17
+ declare function usewagen(options?: WagenPluginOptions): Plugin;
18
+ //#endregion
19
+ export { PARSERS_ID, RESOLVED_PARSERS_ID, WagenPluginOptions, usewagen as default, usewagen };
@@ -0,0 +1,230 @@
1
+ import { g as warn, h as ErrorCodes } from "../parsers-CA8aZuF-.mjs";
2
+ import { n as isBuiltinParserName } from "../builtins-DiCjLgsG.mjs";
3
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
4
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
+ //#region src/vite/generate.ts
6
+ const BANNER = "// Auto-generated by usewagen/vite — do not edit";
7
+ function toPosix(path) {
8
+ return path.split(sep).join("/");
9
+ }
10
+ function toSpecifier(from, file) {
11
+ const target = relative(from, file);
12
+ const specifier = toPosix(join(dirname(target), basename(target, extname(target))));
13
+ return specifier.startsWith(".") ? specifier : `./${specifier}`;
14
+ }
15
+ function generateModule(entries) {
16
+ const byFile = /* @__PURE__ */ new Map();
17
+ for (const { name, file } of entries) {
18
+ const names = byFile.get(file) ?? [];
19
+ names.push(name);
20
+ byFile.set(file, names);
21
+ }
22
+ const lines = [BANNER];
23
+ for (const [file, names] of byFile) lines.push(`import { ${names.join(", ")} } from ${JSON.stringify(toPosix(file))}`);
24
+ lines.push("");
25
+ if (entries.length === 0) {
26
+ lines.push("export const parsers = {}", "");
27
+ return lines.join("\n");
28
+ }
29
+ lines.push("export const parsers = {");
30
+ for (const { name } of entries) lines.push(` ${name},`);
31
+ lines.push("}", "");
32
+ return lines.join("\n");
33
+ }
34
+ function generateDeclaration(entries, dtsPath) {
35
+ const dtsDir = dirname(dtsPath);
36
+ const lines = [
37
+ BANNER,
38
+ "/// <reference types=\"usewagen/client\" />",
39
+ "export {}",
40
+ "",
41
+ "declare module 'usewagen' {",
42
+ " interface CustomParsers {"
43
+ ];
44
+ for (const { name, file } of entries) lines.push(` ${name}: typeof import('${toSpecifier(dtsDir, file)}')['${name}']`);
45
+ lines.push(" }", "}", "");
46
+ return lines.join("\n");
47
+ }
48
+ //#endregion
49
+ //#region src/vite/strip.ts
50
+ const DIVISION_PRECEDING = /[\w$)\]]/;
51
+ function stripNonCode(source) {
52
+ let output = "";
53
+ let index = 0;
54
+ let previous = "";
55
+ const push = (char) => {
56
+ output += char;
57
+ if (!/\s/.test(char)) previous = char;
58
+ };
59
+ while (index < source.length) {
60
+ const char = source[index];
61
+ const next = source[index + 1];
62
+ if (char === "/" && next === "/") {
63
+ while (index < source.length && source[index] !== "\n") index++;
64
+ continue;
65
+ }
66
+ if (char === "/" && next === "*") {
67
+ index += 2;
68
+ while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) {
69
+ if (source[index] === "\n") output += "\n";
70
+ index++;
71
+ }
72
+ index += 2;
73
+ continue;
74
+ }
75
+ if (char === "\"" || char === "'" || char === "`") {
76
+ index++;
77
+ while (index < source.length && source[index] !== char) {
78
+ if (source[index] === "\\") index++;
79
+ index++;
80
+ }
81
+ index++;
82
+ push(char);
83
+ continue;
84
+ }
85
+ if (char === "/" && !DIVISION_PRECEDING.test(previous)) {
86
+ index++;
87
+ let inClass = false;
88
+ while (index < source.length && source[index] !== "\n") {
89
+ const current = source[index];
90
+ if (current === "\\") {
91
+ index += 2;
92
+ continue;
93
+ }
94
+ if (current === "[") inClass = true;
95
+ else if (current === "]") inClass = false;
96
+ else if (current === "/" && !inClass) break;
97
+ index++;
98
+ }
99
+ index++;
100
+ previous = "/";
101
+ continue;
102
+ }
103
+ push(char);
104
+ index++;
105
+ }
106
+ return output;
107
+ }
108
+ //#endregion
109
+ //#region src/vite/scan.ts
110
+ const MODULE_EXTENSIONS = /* @__PURE__ */ new Set([
111
+ ".js",
112
+ ".mjs",
113
+ ".ts",
114
+ ".mts"
115
+ ]);
116
+ const DECLARATION_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".mts"]);
117
+ const PARSER_EXPORT = /export\s+const\s+(parseAs\w+)/g;
118
+ function isDeclarationFile(name, extension) {
119
+ return DECLARATION_EXTENSIONS.has(extension) && basename(name, extension).endsWith(".d");
120
+ }
121
+ function isParserFile(name) {
122
+ const extension = extname(name);
123
+ return MODULE_EXTENSIONS.has(extension) && !isDeclarationFile(name, extension);
124
+ }
125
+ function extractNames(file) {
126
+ const source = stripNonCode(readFileSync(file, "utf-8"));
127
+ const names = [];
128
+ PARSER_EXPORT.lastIndex = 0;
129
+ let match;
130
+ while ((match = PARSER_EXPORT.exec(source)) !== null) {
131
+ const name = match[1];
132
+ if (isBuiltinParserName(name)) {
133
+ warn(ErrorCodes.RESERVED_PARSER_NAME, name, file);
134
+ continue;
135
+ }
136
+ names.push(name);
137
+ }
138
+ return names;
139
+ }
140
+ function walk(dir, entries) {
141
+ if (!existsSync(dir)) return;
142
+ for (const item of readdirSync(dir, { withFileTypes: true })) {
143
+ if (item.isDirectory()) {
144
+ walk(resolve(dir, item.name), entries);
145
+ continue;
146
+ }
147
+ if (!isParserFile(item.name)) continue;
148
+ const file = resolve(dir, item.name);
149
+ for (const name of extractNames(file)) entries.push({
150
+ name,
151
+ file
152
+ });
153
+ }
154
+ }
155
+ function dedupe(entries) {
156
+ const byName = /* @__PURE__ */ new Map();
157
+ for (const entry of entries) {
158
+ const previous = byName.get(entry.name);
159
+ if (previous) warn(ErrorCodes.DUPLICATE_PARSER_NAME, entry.name, previous.file, entry.file);
160
+ byName.set(entry.name, entry);
161
+ }
162
+ return [...byName.values()];
163
+ }
164
+ function scanParsers(dirs) {
165
+ const entries = [];
166
+ for (const dir of dirs) walk(dir, entries);
167
+ return dedupe(entries);
168
+ }
169
+ //#endregion
170
+ //#region src/vite/index.ts
171
+ const PARSERS_ID = "virtual:usewagen/parsers";
172
+ const RESOLVED_PARSERS_ID = `\0${PARSERS_ID}`;
173
+ function contains(dir, path) {
174
+ const target = relative(dir, path);
175
+ return !isAbsolute(target) && !target.startsWith(`..${sep}`);
176
+ }
177
+ function usewagen(options = {}) {
178
+ const { dirs: rawDirs = "src/parsers", dts = "usewagen.d.ts" } = options;
179
+ let dirs = [];
180
+ let dtsPath = "";
181
+ let entries = null;
182
+ function scan() {
183
+ entries ??= scanParsers(dirs);
184
+ return entries;
185
+ }
186
+ function writeDeclaration() {
187
+ if (dts === false) return;
188
+ const content = generateDeclaration(scan(), dtsPath);
189
+ const dir = dirname(dtsPath);
190
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
191
+ if ((existsSync(dtsPath) ? readFileSync(dtsPath, "utf-8") : "") !== content) writeFileSync(dtsPath, content, "utf-8");
192
+ }
193
+ function isScanned(path) {
194
+ return dirs.some((dir) => contains(dir, path)) && isParserFile(basename(path));
195
+ }
196
+ return {
197
+ name: "usewagen",
198
+ configResolved(config) {
199
+ dirs = (Array.isArray(rawDirs) ? rawDirs : [rawDirs]).map((dir) => resolve(config.root, dir));
200
+ if (dts !== false) dtsPath = resolve(config.root, dts);
201
+ entries = null;
202
+ writeDeclaration();
203
+ },
204
+ resolveId(id) {
205
+ if (id === "virtual:usewagen/parsers") return RESOLVED_PARSERS_ID;
206
+ },
207
+ load(id) {
208
+ if (id !== RESOLVED_PARSERS_ID) return;
209
+ const found = scan();
210
+ for (const dir of dirs) this.addWatchFile(dir);
211
+ for (const { file } of found) this.addWatchFile(file);
212
+ return generateModule(found);
213
+ },
214
+ configureServer(server) {
215
+ for (const dir of dirs) server.watcher.add(dir);
216
+ server.watcher.on("all", (event, path) => {
217
+ if (event !== "add" && event !== "unlink" && event !== "change") return;
218
+ if (!isScanned(path)) return;
219
+ entries = null;
220
+ writeDeclaration();
221
+ const mod = server.moduleGraph.getModuleById(RESOLVED_PARSERS_ID);
222
+ if (!mod) return;
223
+ server.moduleGraph.invalidateModule(mod);
224
+ server.ws.send({ type: "full-reload" });
225
+ });
226
+ }
227
+ };
228
+ }
229
+ //#endregion
230
+ export { PARSERS_ID, RESOLVED_PARSERS_ID, usewagen as default, usewagen };