@shrkcrft/core 0.1.0-alpha.2

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/dist/context/execution-context.d.ts +18 -0
  4. package/dist/context/execution-context.d.ts.map +1 -0
  5. package/dist/context/execution-context.js +9 -0
  6. package/dist/fs/bun-file-system.d.ts +22 -0
  7. package/dist/fs/bun-file-system.d.ts.map +1 -0
  8. package/dist/fs/bun-file-system.js +120 -0
  9. package/dist/fs/file-system.d.ts +36 -0
  10. package/dist/fs/file-system.d.ts.map +1 -0
  11. package/dist/fs/file-system.js +1 -0
  12. package/dist/ids/id.d.ts +4 -0
  13. package/dist/ids/id.d.ts.map +1 -0
  14. package/dist/ids/id.js +19 -0
  15. package/dist/index.d.ts +15 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +14 -0
  18. package/dist/load/import-context.d.ts +13 -0
  19. package/dist/load/import-context.d.ts.map +1 -0
  20. package/dist/load/import-context.js +33 -0
  21. package/dist/load/safe-import.d.ts +21 -0
  22. package/dist/load/safe-import.d.ts.map +1 -0
  23. package/dist/load/safe-import.js +55 -0
  24. package/dist/logger/log-level.d.ts +10 -0
  25. package/dist/logger/log-level.d.ts.map +1 -0
  26. package/dist/logger/log-level.js +29 -0
  27. package/dist/logger/logger.d.ts +33 -0
  28. package/dist/logger/logger.d.ts.map +1 -0
  29. package/dist/logger/logger.js +69 -0
  30. package/dist/object/object-utils.d.ts +8 -0
  31. package/dist/object/object-utils.d.ts.map +1 -0
  32. package/dist/object/object-utils.js +69 -0
  33. package/dist/path/path-utils.d.ts +13 -0
  34. package/dist/path/path-utils.d.ts.map +1 -0
  35. package/dist/path/path-utils.js +38 -0
  36. package/dist/path/safe-target-path.d.ts +29 -0
  37. package/dist/path/safe-target-path.d.ts.map +1 -0
  38. package/dist/path/safe-target-path.js +46 -0
  39. package/dist/result/errors.d.ts +43 -0
  40. package/dist/result/errors.d.ts.map +1 -0
  41. package/dist/result/errors.js +47 -0
  42. package/dist/result/result.d.ts +20 -0
  43. package/dist/result/result.d.ts.map +1 -0
  44. package/dist/result/result.js +40 -0
  45. package/dist/string/string-utils.d.ts +11 -0
  46. package/dist/string/string-utils.d.ts.map +1 -0
  47. package/dist/string/string-utils.js +69 -0
  48. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SharkCraft contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # @shrkcrft/core
2
+
3
+ SharkCraft core primitives: Result, AppError, logger, file-system abstraction, path/string/object utils, IDs.
4
+
5
+ Part of [SharkCraft](https://github.com/shrkcrft/sharkcraft) — a deterministic, local-first toolkit that gives AI coding agents durable project context. See the main repo for documentation, examples, and the `shrk` CLI.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ bun add @shrkcrft/core
11
+ ```
12
+
13
+ ## License
14
+
15
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,18 @@
1
+ import type { ILogger } from '../logger/logger.js';
2
+ import type { IFileSystem } from '../fs/file-system.js';
3
+ export interface IExecutionContext {
4
+ readonly cwd: string;
5
+ readonly logger: ILogger;
6
+ readonly fs: IFileSystem;
7
+ readonly env: Readonly<Record<string, string | undefined>>;
8
+ readonly now: () => Date;
9
+ }
10
+ export interface CreateExecutionContextOptions {
11
+ cwd?: string;
12
+ logger: ILogger;
13
+ fs: IFileSystem;
14
+ env?: Record<string, string | undefined>;
15
+ now?: () => Date;
16
+ }
17
+ export declare function createExecutionContext(options: CreateExecutionContextOptions): IExecutionContext;
18
+ //# sourceMappingURL=execution-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../../src/context/execution-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,6BAA6B;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,EAAE,EAAE,WAAW,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,iBAAiB,CAQhG"}
@@ -0,0 +1,9 @@
1
+ export function createExecutionContext(options) {
2
+ return {
3
+ cwd: options.cwd ?? process.cwd(),
4
+ logger: options.logger,
5
+ fs: options.fs,
6
+ env: options.env ?? process.env,
7
+ now: options.now ?? (() => new Date()),
8
+ };
9
+ }
@@ -0,0 +1,22 @@
1
+ import { type Result } from '../result/result.js';
2
+ import { type AppError } from '../result/errors.js';
3
+ import type { DirEntry, FileStat, IFileSystem, RemoveOptions, WriteOptions } from './file-system.js';
4
+ export declare class BunFileSystem implements IFileSystem {
5
+ private readonly _cwd;
6
+ constructor(cwd?: string);
7
+ cwd(): string;
8
+ resolve(...parts: string[]): string;
9
+ relative(from: string, to: string): string;
10
+ exists(path: string): Promise<boolean>;
11
+ isFile(path: string): Promise<boolean>;
12
+ isDirectory(path: string): Promise<boolean>;
13
+ readFile(path: string): Promise<Result<string, AppError>>;
14
+ writeFile(path: string, contents: string, options?: WriteOptions): Promise<Result<void, AppError>>;
15
+ ensureDir(path: string): Promise<Result<void, AppError>>;
16
+ readDir(path: string): Promise<Result<DirEntry[], AppError>>;
17
+ remove(path: string, options?: RemoveOptions): Promise<Result<void, AppError>>;
18
+ stat(path: string): Promise<Result<FileStat, AppError>>;
19
+ }
20
+ export declare function getFileSystem(): IFileSystem;
21
+ export declare function setFileSystem(fs: IFileSystem): void;
22
+ //# sourceMappingURL=bun-file-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun-file-system.d.ts","sourceRoot":"","sources":["../../src/fs/bun-file-system.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAA6B,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,aAAa,EACb,YAAY,EACb,MAAM,kBAAkB,CAAC;AAE1B,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,GAAG,CAAC,EAAE,MAAM;IAIxB,GAAG,IAAI,MAAM;IAIb,OAAO,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAInC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM;IAIpC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAItC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAStC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAezD,SAAS,CACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IA2B5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAexD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC;IAsB5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAelF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;CAmB9D;AAID,wBAAgB,aAAa,IAAI,WAAW,CAG3C;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,CAEnD"}
@@ -0,0 +1,120 @@
1
+ import * as nodePath from 'node:path';
2
+ import * as fs from 'node:fs/promises';
3
+ import { existsSync, statSync } from 'node:fs';
4
+ import { err, ok } from "../result/result.js";
5
+ import { AppErrorImpl, ERROR_CODES } from "../result/errors.js";
6
+ export class BunFileSystem {
7
+ _cwd;
8
+ constructor(cwd) {
9
+ this._cwd = cwd ?? process.cwd();
10
+ }
11
+ cwd() {
12
+ return this._cwd;
13
+ }
14
+ resolve(...parts) {
15
+ return nodePath.resolve(this._cwd, ...parts);
16
+ }
17
+ relative(from, to) {
18
+ return nodePath.relative(from, to);
19
+ }
20
+ async exists(path) {
21
+ return existsSync(path);
22
+ }
23
+ async isFile(path) {
24
+ try {
25
+ const st = statSync(path);
26
+ return st.isFile();
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ async isDirectory(path) {
33
+ try {
34
+ const st = statSync(path);
35
+ return st.isDirectory();
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ async readFile(path) {
42
+ try {
43
+ const text = await fs.readFile(path, 'utf8');
44
+ return ok(text);
45
+ }
46
+ catch (e) {
47
+ return err(new AppErrorImpl(ERROR_CODES.FILE_READ_ERROR, `Failed to read file: ${path}`, { details: { path }, cause: e }));
48
+ }
49
+ }
50
+ async writeFile(path, contents, options = {}) {
51
+ try {
52
+ if (options.createDirs !== false) {
53
+ await fs.mkdir(nodePath.dirname(path), { recursive: true });
54
+ }
55
+ if (options.overwrite === false && existsSync(path)) {
56
+ return err(new AppErrorImpl(ERROR_CODES.TARGET_FILE_EXISTS, `Target file exists: ${path}`, { details: { path }, suggestion: 'Pass overwrite:true or use --force' }));
57
+ }
58
+ await fs.writeFile(path, contents, 'utf8');
59
+ return ok(undefined);
60
+ }
61
+ catch (e) {
62
+ return err(new AppErrorImpl(ERROR_CODES.FILE_WRITE_ERROR, `Failed to write file: ${path}`, { details: { path }, cause: e }));
63
+ }
64
+ }
65
+ async ensureDir(path) {
66
+ try {
67
+ await fs.mkdir(path, { recursive: true });
68
+ return ok(undefined);
69
+ }
70
+ catch (e) {
71
+ return err(new AppErrorImpl(ERROR_CODES.IO_ERROR, `Failed to ensure directory: ${path}`, { details: { path }, cause: e }));
72
+ }
73
+ }
74
+ async readDir(path) {
75
+ try {
76
+ const entries = await fs.readdir(path, { withFileTypes: true });
77
+ return ok(entries.map((e) => ({
78
+ name: e.name,
79
+ isDirectory: e.isDirectory(),
80
+ isFile: e.isFile(),
81
+ isSymlink: e.isSymbolicLink(),
82
+ })));
83
+ }
84
+ catch (e) {
85
+ return err(new AppErrorImpl(ERROR_CODES.IO_ERROR, `Failed to read directory: ${path}`, { details: { path }, cause: e }));
86
+ }
87
+ }
88
+ async remove(path, options = {}) {
89
+ try {
90
+ await fs.rm(path, { recursive: options.recursive ?? false, force: true });
91
+ return ok(undefined);
92
+ }
93
+ catch (e) {
94
+ return err(new AppErrorImpl(ERROR_CODES.IO_ERROR, `Failed to remove: ${path}`, { details: { path }, cause: e }));
95
+ }
96
+ }
97
+ async stat(path) {
98
+ try {
99
+ const st = await fs.stat(path);
100
+ return ok({
101
+ size: st.size,
102
+ isFile: st.isFile(),
103
+ isDirectory: st.isDirectory(),
104
+ mtimeMs: st.mtimeMs,
105
+ });
106
+ }
107
+ catch (e) {
108
+ return err(new AppErrorImpl(ERROR_CODES.IO_ERROR, `Failed to stat: ${path}`, { details: { path }, cause: e }));
109
+ }
110
+ }
111
+ }
112
+ let defaultFs = null;
113
+ export function getFileSystem() {
114
+ if (!defaultFs)
115
+ defaultFs = new BunFileSystem();
116
+ return defaultFs;
117
+ }
118
+ export function setFileSystem(fs) {
119
+ defaultFs = fs;
120
+ }
@@ -0,0 +1,36 @@
1
+ import type { Result } from '../result/result.js';
2
+ import type { AppError } from '../result/errors.js';
3
+ export interface DirEntry {
4
+ name: string;
5
+ isDirectory: boolean;
6
+ isFile: boolean;
7
+ isSymlink: boolean;
8
+ }
9
+ export interface IFileSystem {
10
+ exists(path: string): Promise<boolean>;
11
+ isFile(path: string): Promise<boolean>;
12
+ isDirectory(path: string): Promise<boolean>;
13
+ readFile(path: string): Promise<Result<string, AppError>>;
14
+ writeFile(path: string, contents: string, options?: WriteOptions): Promise<Result<void, AppError>>;
15
+ ensureDir(path: string): Promise<Result<void, AppError>>;
16
+ readDir(path: string): Promise<Result<DirEntry[], AppError>>;
17
+ remove(path: string, options?: RemoveOptions): Promise<Result<void, AppError>>;
18
+ stat(path: string): Promise<Result<FileStat, AppError>>;
19
+ cwd(): string;
20
+ resolve(...parts: string[]): string;
21
+ relative(from: string, to: string): string;
22
+ }
23
+ export interface WriteOptions {
24
+ overwrite?: boolean;
25
+ createDirs?: boolean;
26
+ }
27
+ export interface RemoveOptions {
28
+ recursive?: boolean;
29
+ }
30
+ export interface FileStat {
31
+ size: number;
32
+ isFile: boolean;
33
+ isDirectory: boolean;
34
+ mtimeMs: number;
35
+ }
36
+ //# sourceMappingURL=file-system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../../src/fs/file-system.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC1D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/E,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,GAAG,IAAI,MAAM,CAAC;IACd,OAAO,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5C;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ export declare function generateId(prefix?: string): string;
2
+ export declare function isValidKnowledgeId(id: string): boolean;
3
+ export declare function normalizeKnowledgeId(id: string): string;
4
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/ids/id.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,CAAC,MAAM,SAAO,GAAG,MAAM,CAKhD;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAOvD"}
package/dist/ids/id.js ADDED
@@ -0,0 +1,19 @@
1
+ let monotonicCounter = 0;
2
+ export function generateId(prefix = 'id') {
3
+ monotonicCounter += 1;
4
+ const ts = Date.now().toString(36);
5
+ const rand = Math.floor(Math.random() * 1e9).toString(36);
6
+ return `${prefix}_${ts}_${rand}_${monotonicCounter.toString(36)}`;
7
+ }
8
+ const KNOWLEDGE_ID_RE = /^[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
9
+ export function isValidKnowledgeId(id) {
10
+ return KNOWLEDGE_ID_RE.test(id);
11
+ }
12
+ export function normalizeKnowledgeId(id) {
13
+ return id
14
+ .trim()
15
+ .toLowerCase()
16
+ .replace(/[\s_]+/g, '-')
17
+ .replace(/[^a-z0-9.-]/g, '')
18
+ .replace(/^[-.]+|[-.]+$/g, '');
19
+ }
@@ -0,0 +1,15 @@
1
+ export * from './result/result.js';
2
+ export * from './result/errors.js';
3
+ export * from './logger/log-level.js';
4
+ export * from './logger/logger.js';
5
+ export * from './fs/file-system.js';
6
+ export * from './fs/bun-file-system.js';
7
+ export * from './path/path-utils.js';
8
+ export * from './path/safe-target-path.js';
9
+ export * from './string/string-utils.js';
10
+ export * from './object/object-utils.js';
11
+ export * from './ids/id.js';
12
+ export * from './context/execution-context.js';
13
+ export * from './load/safe-import.js';
14
+ export * from './load/import-context.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,aAAa,CAAC;AAC5B,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./result/result.js";
2
+ export * from "./result/errors.js";
3
+ export * from "./logger/log-level.js";
4
+ export * from "./logger/logger.js";
5
+ export * from "./fs/file-system.js";
6
+ export * from "./fs/bun-file-system.js";
7
+ export * from "./path/path-utils.js";
8
+ export * from "./path/safe-target-path.js";
9
+ export * from "./string/string-utils.js";
10
+ export * from "./object/object-utils.js";
11
+ export * from "./ids/id.js";
12
+ export * from "./context/execution-context.js";
13
+ export * from "./load/safe-import.js";
14
+ export * from "./load/import-context.js";
@@ -0,0 +1,13 @@
1
+ import { type SafeImportResult } from './safe-import.js';
2
+ export interface IImportContext {
3
+ readonly timeoutMs: number;
4
+ size(): number;
5
+ load<T = Record<string, unknown>>(filePath: string): Promise<SafeImportResult<T>>;
6
+ hasSettled(filePath: string): boolean;
7
+ entries(): IterableIterator<[string, SafeImportResult]>;
8
+ }
9
+ export interface IImportContextOptions {
10
+ timeoutMs?: number;
11
+ }
12
+ export declare function createImportContext(options?: IImportContextOptions): IImportContext;
13
+ //# sourceMappingURL=import-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-context.d.ts","sourceRoot":"","sources":["../../src/load/import-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,IAAI,IAAI,MAAM,CAAC;IACf,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAClF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACtC,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoCD,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAEvF"}
@@ -0,0 +1,33 @@
1
+ import { DEFAULT_SAFE_IMPORT_TIMEOUT_MS, safeImport, } from "./safe-import.js";
2
+ class ImportContext {
3
+ timeoutMs;
4
+ _pending = new Map();
5
+ _settled = new Map();
6
+ constructor(options = {}) {
7
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_SAFE_IMPORT_TIMEOUT_MS;
8
+ }
9
+ size() {
10
+ return this._pending.size;
11
+ }
12
+ hasSettled(filePath) {
13
+ return this._settled.has(filePath);
14
+ }
15
+ async load(filePath) {
16
+ const existing = this._pending.get(filePath);
17
+ if (existing)
18
+ return existing;
19
+ const p = safeImport(filePath, { timeoutMs: this.timeoutMs });
20
+ const wrapped = p.then((r) => {
21
+ this._settled.set(filePath, r);
22
+ return r;
23
+ });
24
+ this._pending.set(filePath, wrapped);
25
+ return wrapped;
26
+ }
27
+ entries() {
28
+ return this._settled.entries();
29
+ }
30
+ }
31
+ export function createImportContext(options = {}) {
32
+ return new ImportContext(options);
33
+ }
@@ -0,0 +1,21 @@
1
+ export interface ISafeImportSuccess<T = Record<string, unknown>> {
2
+ ok: true;
3
+ module: T;
4
+ elapsedMs: number;
5
+ }
6
+ export interface ISafeImportFailure {
7
+ ok: false;
8
+ error: Error;
9
+ elapsedMs: number;
10
+ timedOut: boolean;
11
+ }
12
+ export type SafeImportResult<T = Record<string, unknown>> = ISafeImportSuccess<T> | ISafeImportFailure;
13
+ export interface ISafeImportOptions {
14
+ /** Max ms to wait for the dynamic import before timing out. */
15
+ timeoutMs?: number;
16
+ /** Skip the existsSync pre-check (caller already verified). */
17
+ skipExistsCheck?: boolean;
18
+ }
19
+ export declare const DEFAULT_SAFE_IMPORT_TIMEOUT_MS = 8000;
20
+ export declare function safeImport<T = Record<string, unknown>>(filePath: string, options?: ISafeImportOptions): Promise<SafeImportResult<T>>;
21
+ //# sourceMappingURL=safe-import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-import.d.ts","sourceRoot":"","sources":["../../src/load/safe-import.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC7D,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,CAAC,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACpD,kBAAkB,CAAC,CAAC,CAAC,GACrB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD,wBAAsB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CA+C9B"}
@@ -0,0 +1,55 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync } from 'node:fs';
10
+ import { pathToFileURL } from 'node:url';
11
+ export const DEFAULT_SAFE_IMPORT_TIMEOUT_MS = 8000;
12
+ export async function safeImport(filePath, options = {}) {
13
+ const timeoutMs = options.timeoutMs ?? DEFAULT_SAFE_IMPORT_TIMEOUT_MS;
14
+ const start = Date.now();
15
+ if (!options.skipExistsCheck && !existsSync(filePath)) {
16
+ return {
17
+ ok: false,
18
+ error: new Error(`file not found: ${filePath}`),
19
+ elapsedMs: Date.now() - start,
20
+ timedOut: false,
21
+ };
22
+ }
23
+ let timer = null;
24
+ const timeoutPromise = new Promise((resolve) => {
25
+ timer = setTimeout(() => {
26
+ resolve({
27
+ ok: false,
28
+ error: new Error(`import timed out after ${timeoutMs}ms: ${filePath}`),
29
+ elapsedMs: Date.now() - start,
30
+ timedOut: true,
31
+ });
32
+ }, timeoutMs);
33
+ if (timer && typeof timer.unref === 'function') {
34
+ timer.unref();
35
+ }
36
+ });
37
+ const importPromise = (async () => {
38
+ try {
39
+ const mod = (await import(__rewriteRelativeImportExtension(pathToFileURL(filePath).href)));
40
+ return { ok: true, module: mod, elapsedMs: Date.now() - start };
41
+ }
42
+ catch (e) {
43
+ return {
44
+ ok: false,
45
+ error: e instanceof Error ? e : new Error(String(e)),
46
+ elapsedMs: Date.now() - start,
47
+ timedOut: false,
48
+ };
49
+ }
50
+ })();
51
+ const result = await Promise.race([importPromise, timeoutPromise]);
52
+ if (timer)
53
+ clearTimeout(timer);
54
+ return result;
55
+ }
@@ -0,0 +1,10 @@
1
+ export declare enum LogLevel {
2
+ Silent = 0,
3
+ Error = 1,
4
+ Warn = 2,
5
+ Info = 3,
6
+ Debug = 4,
7
+ Trace = 5
8
+ }
9
+ export declare function parseLogLevel(value: string | undefined, fallback?: LogLevel): LogLevel;
10
+ //# sourceMappingURL=log-level.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-level.d.ts","sourceRoot":"","sources":["../../src/logger/log-level.ts"],"names":[],"mappings":"AAAA,oBAAY,QAAQ;IAClB,MAAM,IAAI;IACV,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;IACT,KAAK,IAAI;CACV;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,GAAE,QAAwB,GAAG,QAAQ,CAkBrG"}
@@ -0,0 +1,29 @@
1
+ export var LogLevel;
2
+ (function (LogLevel) {
3
+ LogLevel[LogLevel["Silent"] = 0] = "Silent";
4
+ LogLevel[LogLevel["Error"] = 1] = "Error";
5
+ LogLevel[LogLevel["Warn"] = 2] = "Warn";
6
+ LogLevel[LogLevel["Info"] = 3] = "Info";
7
+ LogLevel[LogLevel["Debug"] = 4] = "Debug";
8
+ LogLevel[LogLevel["Trace"] = 5] = "Trace";
9
+ })(LogLevel || (LogLevel = {}));
10
+ export function parseLogLevel(value, fallback = LogLevel.Info) {
11
+ if (!value)
12
+ return fallback;
13
+ switch (value.toLowerCase()) {
14
+ case 'silent':
15
+ return LogLevel.Silent;
16
+ case 'error':
17
+ return LogLevel.Error;
18
+ case 'warn':
19
+ return LogLevel.Warn;
20
+ case 'info':
21
+ return LogLevel.Info;
22
+ case 'debug':
23
+ return LogLevel.Debug;
24
+ case 'trace':
25
+ return LogLevel.Trace;
26
+ default:
27
+ return fallback;
28
+ }
29
+ }
@@ -0,0 +1,33 @@
1
+ import { LogLevel } from './log-level.js';
2
+ export interface ILogger {
3
+ level: LogLevel;
4
+ error(message: string, meta?: Record<string, unknown>): void;
5
+ warn(message: string, meta?: Record<string, unknown>): void;
6
+ info(message: string, meta?: Record<string, unknown>): void;
7
+ debug(message: string, meta?: Record<string, unknown>): void;
8
+ trace(message: string, meta?: Record<string, unknown>): void;
9
+ child(scope: string): ILogger;
10
+ }
11
+ export interface LoggerOptions {
12
+ level?: LogLevel;
13
+ scope?: string;
14
+ writer?: (line: string) => void;
15
+ json?: boolean;
16
+ }
17
+ export declare class Logger implements ILogger {
18
+ level: LogLevel;
19
+ private readonly scope;
20
+ private readonly writer;
21
+ private readonly json;
22
+ constructor(options?: LoggerOptions);
23
+ error(message: string, meta?: Record<string, unknown>): void;
24
+ warn(message: string, meta?: Record<string, unknown>): void;
25
+ info(message: string, meta?: Record<string, unknown>): void;
26
+ debug(message: string, meta?: Record<string, unknown>): void;
27
+ trace(message: string, meta?: Record<string, unknown>): void;
28
+ child(scope: string): ILogger;
29
+ private emit;
30
+ }
31
+ export declare function getDefaultLogger(): ILogger;
32
+ export declare function setDefaultLogger(logger: ILogger): void;
33
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,MAAO,YAAW,OAAO;IACpC,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;gBAEnB,OAAO,GAAE,aAAkB;IAOvC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAG5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAG3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAG3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAG5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAW7B,OAAO,CAAC,IAAI;CAgBb;AAID,wBAAgB,gBAAgB,IAAI,OAAO,CAK1C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAEtD"}
@@ -0,0 +1,69 @@
1
+ import { LogLevel } from "./log-level.js";
2
+ export class Logger {
3
+ level;
4
+ scope;
5
+ writer;
6
+ json;
7
+ constructor(options = {}) {
8
+ this.level = options.level ?? LogLevel.Info;
9
+ this.scope = options.scope ?? '';
10
+ this.writer = options.writer ?? ((line) => process.stderr.write(line + '\n'));
11
+ this.json = options.json ?? false;
12
+ }
13
+ error(message, meta) {
14
+ if (this.level >= LogLevel.Error)
15
+ this.emit('error', message, meta);
16
+ }
17
+ warn(message, meta) {
18
+ if (this.level >= LogLevel.Warn)
19
+ this.emit('warn', message, meta);
20
+ }
21
+ info(message, meta) {
22
+ if (this.level >= LogLevel.Info)
23
+ this.emit('info', message, meta);
24
+ }
25
+ debug(message, meta) {
26
+ if (this.level >= LogLevel.Debug)
27
+ this.emit('debug', message, meta);
28
+ }
29
+ trace(message, meta) {
30
+ if (this.level >= LogLevel.Trace)
31
+ this.emit('trace', message, meta);
32
+ }
33
+ child(scope) {
34
+ const nested = this.scope ? `${this.scope}:${scope}` : scope;
35
+ const child = new Logger({
36
+ level: this.level,
37
+ scope: nested,
38
+ writer: this.writer,
39
+ json: this.json,
40
+ });
41
+ return child;
42
+ }
43
+ emit(level, message, meta) {
44
+ if (this.json) {
45
+ const payload = {
46
+ ts: new Date().toISOString(),
47
+ level,
48
+ scope: this.scope || undefined,
49
+ message,
50
+ ...(meta ? { meta } : {}),
51
+ };
52
+ this.writer(JSON.stringify(payload));
53
+ return;
54
+ }
55
+ const prefix = this.scope ? `[${level}] [${this.scope}]` : `[${level}]`;
56
+ const metaStr = meta && Object.keys(meta).length ? ' ' + JSON.stringify(meta) : '';
57
+ this.writer(`${prefix} ${message}${metaStr}`);
58
+ }
59
+ }
60
+ let defaultLogger = null;
61
+ export function getDefaultLogger() {
62
+ if (!defaultLogger) {
63
+ defaultLogger = new Logger({ level: LogLevel.Info });
64
+ }
65
+ return defaultLogger;
66
+ }
67
+ export function setDefaultLogger(logger) {
68
+ defaultLogger = logger;
69
+ }
@@ -0,0 +1,8 @@
1
+ export declare function deepFreeze<T>(value: T): Readonly<T>;
2
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
3
+ export declare function merge<A extends Record<string, unknown>, B extends Record<string, unknown>>(a: A, b: B): A & B;
4
+ export declare function pick<T extends object, K extends keyof T>(obj: T, keys: readonly K[]): Pick<T, K>;
5
+ export declare function omit<T extends object, K extends keyof T>(obj: T, keys: readonly K[]): Omit<T, K>;
6
+ export declare function uniqueBy<T, K>(items: readonly T[], keyFn: (item: T) => K): T[];
7
+ export declare function groupBy<T, K extends string | number>(items: readonly T[], keyFn: (item: T) => K): Record<K, T[]>;
8
+ //# sourceMappingURL=object-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-utils.d.ts","sourceRoot":"","sources":["../../src/object/object-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CASnD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI9E;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxF,CAAC,EAAE,CAAC,EACJ,CAAC,EAAE,CAAC,GACH,CAAC,GAAG,CAAC,CAYP;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAMhG;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAShG;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAW9E;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EAClD,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GACpB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAOhB"}
@@ -0,0 +1,69 @@
1
+ export function deepFreeze(value) {
2
+ if (value === null || typeof value !== 'object')
3
+ return value;
4
+ for (const key of Object.keys(value)) {
5
+ const v = value[key];
6
+ if (v && typeof v === 'object' && !Object.isFrozen(v)) {
7
+ deepFreeze(v);
8
+ }
9
+ }
10
+ return Object.freeze(value);
11
+ }
12
+ export function isPlainObject(value) {
13
+ if (typeof value !== 'object' || value === null)
14
+ return false;
15
+ const proto = Object.getPrototypeOf(value);
16
+ return proto === null || proto === Object.prototype;
17
+ }
18
+ export function merge(a, b) {
19
+ const result = { ...a };
20
+ for (const key of Object.keys(b)) {
21
+ const av = result[key];
22
+ const bv = b[key];
23
+ if (isPlainObject(av) && isPlainObject(bv)) {
24
+ result[key] = merge(av, bv);
25
+ }
26
+ else if (bv !== undefined) {
27
+ result[key] = bv;
28
+ }
29
+ }
30
+ return result;
31
+ }
32
+ export function pick(obj, keys) {
33
+ const result = {};
34
+ for (const key of keys) {
35
+ if (key in obj)
36
+ result[key] = obj[key];
37
+ }
38
+ return result;
39
+ }
40
+ export function omit(obj, keys) {
41
+ const keySet = new Set(keys);
42
+ const result = {};
43
+ for (const key of Object.keys(obj)) {
44
+ if (!keySet.has(key)) {
45
+ result[key] = obj[key];
46
+ }
47
+ }
48
+ return result;
49
+ }
50
+ export function uniqueBy(items, keyFn) {
51
+ const seen = new Set();
52
+ const out = [];
53
+ for (const item of items) {
54
+ const key = keyFn(item);
55
+ if (!seen.has(key)) {
56
+ seen.add(key);
57
+ out.push(item);
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ export function groupBy(items, keyFn) {
63
+ const out = {};
64
+ for (const item of items) {
65
+ const key = keyFn(item);
66
+ (out[key] ??= []).push(item);
67
+ }
68
+ return out;
69
+ }
@@ -0,0 +1,13 @@
1
+ export declare function joinPath(...parts: string[]): string;
2
+ export declare function resolvePath(...parts: string[]): string;
3
+ export declare function normalizePath(p: string): string;
4
+ export declare function isAbsolutePath(p: string): boolean;
5
+ export declare function basename(p: string, ext?: string): string;
6
+ export declare function dirname(p: string): string;
7
+ export declare function extname(p: string): string;
8
+ export declare function relativePath(from: string, to: string): string;
9
+ export declare function isPathInside(child: string, parent: string): boolean;
10
+ export declare function ensureTrailingSlash(p: string): string;
11
+ export declare function stripTrailingSlash(p: string): string;
12
+ export declare function toPosix(p: string): string;
13
+ //# sourceMappingURL=path-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/path/path-utils.ts"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEnD;AAED,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC"}
@@ -0,0 +1,38 @@
1
+ import * as nodePath from 'node:path';
2
+ export function joinPath(...parts) {
3
+ return nodePath.join(...parts);
4
+ }
5
+ export function resolvePath(...parts) {
6
+ return nodePath.resolve(...parts);
7
+ }
8
+ export function normalizePath(p) {
9
+ return nodePath.normalize(p);
10
+ }
11
+ export function isAbsolutePath(p) {
12
+ return nodePath.isAbsolute(p);
13
+ }
14
+ export function basename(p, ext) {
15
+ return ext === undefined ? nodePath.basename(p) : nodePath.basename(p, ext);
16
+ }
17
+ export function dirname(p) {
18
+ return nodePath.dirname(p);
19
+ }
20
+ export function extname(p) {
21
+ return nodePath.extname(p);
22
+ }
23
+ export function relativePath(from, to) {
24
+ return nodePath.relative(from, to);
25
+ }
26
+ export function isPathInside(child, parent) {
27
+ const rel = nodePath.relative(parent, child);
28
+ return rel !== '' && !rel.startsWith('..') && !nodePath.isAbsolute(rel);
29
+ }
30
+ export function ensureTrailingSlash(p) {
31
+ return p.endsWith('/') ? p : p + '/';
32
+ }
33
+ export function stripTrailingSlash(p) {
34
+ return p.length > 1 && p.endsWith('/') ? p.slice(0, -1) : p;
35
+ }
36
+ export function toPosix(p) {
37
+ return p.split(nodePath.sep).join('/');
38
+ }
@@ -0,0 +1,29 @@
1
+ export interface ISafePathResult {
2
+ /** Resolved absolute path. */
3
+ absolutePath: string;
4
+ /** Relative path from projectRoot to absolutePath. */
5
+ relativePath: string;
6
+ }
7
+ export declare class UnsafeTargetPathError extends Error {
8
+ readonly code: 'absolute-path-rejected' | 'traversal-detected' | 'outside-project-root' | 'empty-path';
9
+ readonly rawPath: string;
10
+ constructor(code: UnsafeTargetPathError['code'], rawPath: string, message: string);
11
+ }
12
+ /**
13
+ * Resolve a template-supplied target path against the project root, refusing
14
+ * anything that escapes the project boundary.
15
+ *
16
+ * Rules (in order):
17
+ * 1. Empty / non-string input → reject.
18
+ * 2. Absolute paths → reject unless `allowAbsolute: true`.
19
+ * 3. Normalize the path (collapses ../ and ./).
20
+ * 4. The resolved path must be inside (or equal to) projectRoot.
21
+ *
22
+ * This is the single chokepoint for all generator file writes.
23
+ */
24
+ export declare function safeResolveTargetPath(rawPath: string, projectRoot: string, options?: {
25
+ allowAbsolute?: boolean;
26
+ }): ISafePathResult;
27
+ /** True if a string contains `..` segments. Used for early diagnostics. */
28
+ export declare function containsTraversal(rawPath: string): boolean;
29
+ //# sourceMappingURL=safe-target-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-target-path.d.ts","sourceRoot":"","sources":["../../src/path/safe-target-path.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EACT,wBAAwB,GACxB,oBAAoB,GACpB,sBAAsB,GACtB,YAAY,CAAC;IACjB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGvB,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM;CAOlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,eAAe,CA+BjB;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE1D"}
@@ -0,0 +1,46 @@
1
+ import * as nodePath from 'node:path';
2
+ export class UnsafeTargetPathError extends Error {
3
+ code;
4
+ rawPath;
5
+ constructor(code, rawPath, message) {
6
+ super(message);
7
+ this.name = 'UnsafeTargetPathError';
8
+ this.code = code;
9
+ this.rawPath = rawPath;
10
+ }
11
+ }
12
+ /**
13
+ * Resolve a template-supplied target path against the project root, refusing
14
+ * anything that escapes the project boundary.
15
+ *
16
+ * Rules (in order):
17
+ * 1. Empty / non-string input → reject.
18
+ * 2. Absolute paths → reject unless `allowAbsolute: true`.
19
+ * 3. Normalize the path (collapses ../ and ./).
20
+ * 4. The resolved path must be inside (or equal to) projectRoot.
21
+ *
22
+ * This is the single chokepoint for all generator file writes.
23
+ */
24
+ export function safeResolveTargetPath(rawPath, projectRoot, options = {}) {
25
+ if (typeof rawPath !== 'string' || rawPath.length === 0) {
26
+ throw new UnsafeTargetPathError('empty-path', String(rawPath), 'Target path is empty');
27
+ }
28
+ const resolvedProjectRoot = nodePath.resolve(projectRoot);
29
+ const normalized = nodePath.normalize(rawPath);
30
+ if (nodePath.isAbsolute(normalized) && !options.allowAbsolute) {
31
+ throw new UnsafeTargetPathError('absolute-path-rejected', rawPath, `Absolute target path "${rawPath}" is not allowed without explicit opt-in`);
32
+ }
33
+ const absolute = nodePath.isAbsolute(normalized)
34
+ ? normalized
35
+ : nodePath.resolve(resolvedProjectRoot, normalized);
36
+ // Final containment check — protects against ../../escape and symlink-style tricks.
37
+ const relative = nodePath.relative(resolvedProjectRoot, absolute);
38
+ if (relative === '' || (!relative.startsWith('..') && !nodePath.isAbsolute(relative))) {
39
+ return { absolutePath: absolute, relativePath: relative === '' ? '.' : relative };
40
+ }
41
+ throw new UnsafeTargetPathError('outside-project-root', rawPath, `Target path "${rawPath}" resolves to "${absolute}" which is outside project root "${resolvedProjectRoot}"`);
42
+ }
43
+ /** True if a string contains `..` segments. Used for early diagnostics. */
44
+ export function containsTraversal(rawPath) {
45
+ return nodePath.normalize(rawPath).split(/[\\/]+/).includes('..');
46
+ }
@@ -0,0 +1,43 @@
1
+ export declare const ERROR_CODES: {
2
+ readonly UNKNOWN: "SHRK_UNKNOWN";
3
+ readonly INVALID_INPUT: "SHRK_INVALID_INPUT";
4
+ readonly NOT_FOUND: "SHRK_NOT_FOUND";
5
+ readonly ALREADY_EXISTS: "SHRK_ALREADY_EXISTS";
6
+ readonly CONFIG_NOT_FOUND: "SHRK_CONFIG_NOT_FOUND";
7
+ readonly CONFIG_INVALID: "SHRK_CONFIG_INVALID";
8
+ readonly PROJECT_ROOT_NOT_FOUND: "SHRK_PROJECT_ROOT_NOT_FOUND";
9
+ readonly SHARKCRAFT_FOLDER_NOT_FOUND: "SHRK_FOLDER_NOT_FOUND";
10
+ readonly PACKAGE_JSON_NOT_FOUND: "SHRK_PACKAGE_JSON_NOT_FOUND";
11
+ readonly KNOWLEDGE_ENTRY_NOT_FOUND: "SHRK_KNOWLEDGE_NOT_FOUND";
12
+ readonly KNOWLEDGE_DUPLICATE_ID: "SHRK_KNOWLEDGE_DUPLICATE_ID";
13
+ readonly TEMPLATE_NOT_FOUND: "SHRK_TEMPLATE_NOT_FOUND";
14
+ readonly TEMPLATE_VARIABLE_MISSING: "SHRK_TEMPLATE_VAR_MISSING";
15
+ readonly TARGET_FILE_EXISTS: "SHRK_TARGET_EXISTS";
16
+ readonly PATH_OUTSIDE_PROJECT: "SHRK_PATH_OUTSIDE_PROJECT";
17
+ readonly MCP_INVALID_INPUT: "SHRK_MCP_INVALID_INPUT";
18
+ readonly UNSUPPORTED_COMMAND: "SHRK_UNSUPPORTED_COMMAND";
19
+ readonly FILE_READ_ERROR: "SHRK_FILE_READ_ERROR";
20
+ readonly FILE_WRITE_ERROR: "SHRK_FILE_WRITE_ERROR";
21
+ readonly LOADER_ERROR: "SHRK_LOADER_ERROR";
22
+ readonly IO_ERROR: "SHRK_IO_ERROR";
23
+ };
24
+ export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
25
+ export interface AppError extends Error {
26
+ readonly code: ErrorCode;
27
+ readonly details?: Record<string, unknown> | undefined;
28
+ readonly suggestion?: string | undefined;
29
+ }
30
+ export interface AppErrorOptions {
31
+ details?: Record<string, unknown>;
32
+ suggestion?: string;
33
+ cause?: unknown;
34
+ }
35
+ export declare class AppErrorImpl extends Error implements AppError {
36
+ readonly code: ErrorCode;
37
+ readonly details?: Record<string, unknown> | undefined;
38
+ readonly suggestion?: string | undefined;
39
+ constructor(code: ErrorCode, message: string, options?: AppErrorOptions);
40
+ toJSON(): Record<string, unknown>;
41
+ }
42
+ export declare function makeError(code: ErrorCode, message: string, options?: AppErrorOptions): AppError;
43
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/result/errors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;CAsBd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEvE,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,YAAa,SAAQ,KAAM,YAAW,QAAQ;IACzD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE7B,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;IAQ3E,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASlC;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,eAAoB,GAC5B,QAAQ,CAEV"}
@@ -0,0 +1,47 @@
1
+ export const ERROR_CODES = {
2
+ UNKNOWN: 'SHRK_UNKNOWN',
3
+ INVALID_INPUT: 'SHRK_INVALID_INPUT',
4
+ NOT_FOUND: 'SHRK_NOT_FOUND',
5
+ ALREADY_EXISTS: 'SHRK_ALREADY_EXISTS',
6
+ CONFIG_NOT_FOUND: 'SHRK_CONFIG_NOT_FOUND',
7
+ CONFIG_INVALID: 'SHRK_CONFIG_INVALID',
8
+ PROJECT_ROOT_NOT_FOUND: 'SHRK_PROJECT_ROOT_NOT_FOUND',
9
+ SHARKCRAFT_FOLDER_NOT_FOUND: 'SHRK_FOLDER_NOT_FOUND',
10
+ PACKAGE_JSON_NOT_FOUND: 'SHRK_PACKAGE_JSON_NOT_FOUND',
11
+ KNOWLEDGE_ENTRY_NOT_FOUND: 'SHRK_KNOWLEDGE_NOT_FOUND',
12
+ KNOWLEDGE_DUPLICATE_ID: 'SHRK_KNOWLEDGE_DUPLICATE_ID',
13
+ TEMPLATE_NOT_FOUND: 'SHRK_TEMPLATE_NOT_FOUND',
14
+ TEMPLATE_VARIABLE_MISSING: 'SHRK_TEMPLATE_VAR_MISSING',
15
+ TARGET_FILE_EXISTS: 'SHRK_TARGET_EXISTS',
16
+ PATH_OUTSIDE_PROJECT: 'SHRK_PATH_OUTSIDE_PROJECT',
17
+ MCP_INVALID_INPUT: 'SHRK_MCP_INVALID_INPUT',
18
+ UNSUPPORTED_COMMAND: 'SHRK_UNSUPPORTED_COMMAND',
19
+ FILE_READ_ERROR: 'SHRK_FILE_READ_ERROR',
20
+ FILE_WRITE_ERROR: 'SHRK_FILE_WRITE_ERROR',
21
+ LOADER_ERROR: 'SHRK_LOADER_ERROR',
22
+ IO_ERROR: 'SHRK_IO_ERROR',
23
+ };
24
+ export class AppErrorImpl extends Error {
25
+ code;
26
+ details;
27
+ suggestion;
28
+ constructor(code, message, options = {}) {
29
+ super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
30
+ this.name = 'AppError';
31
+ this.code = code;
32
+ this.details = options.details;
33
+ this.suggestion = options.suggestion;
34
+ }
35
+ toJSON() {
36
+ return {
37
+ name: this.name,
38
+ code: this.code,
39
+ message: this.message,
40
+ details: this.details,
41
+ suggestion: this.suggestion,
42
+ };
43
+ }
44
+ }
45
+ export function makeError(code, message, options = {}) {
46
+ return new AppErrorImpl(code, message, options);
47
+ }
@@ -0,0 +1,20 @@
1
+ import { type AppError } from './errors.js';
2
+ export type Result<T, E = AppError> = ResultOk<T> | ResultErr<E>;
3
+ export interface ResultOk<T> {
4
+ readonly ok: true;
5
+ readonly value: T;
6
+ }
7
+ export interface ResultErr<E> {
8
+ readonly ok: false;
9
+ readonly error: E;
10
+ }
11
+ export declare function ok<T>(value: T): ResultOk<T>;
12
+ export declare function err<E>(error: E): ResultErr<E>;
13
+ export declare function isOk<T, E>(r: Result<T, E>): r is ResultOk<T>;
14
+ export declare function isErr<T, E>(r: Result<T, E>): r is ResultErr<E>;
15
+ export declare function unwrap<T, E>(r: Result<T, E>): T;
16
+ export declare function map<T, U, E>(r: Result<T, E>, fn: (value: T) => U): Result<U, E>;
17
+ export declare function flatMap<T, U, E>(r: Result<T, E>, fn: (value: T) => Result<U, E>): Result<U, E>;
18
+ export declare function tryAsync<T>(fn: () => Promise<T>): Promise<Result<T, AppError>>;
19
+ export declare function trySync<T>(fn: () => T): Result<T, AppError>;
20
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/result/result.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAEjE,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED,wBAAgB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAE3C;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAE7C;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAE5D;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAE9D;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAG/C;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAE/E;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAE9F;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAYpF;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAY3D"}
@@ -0,0 +1,40 @@
1
+ import { AppErrorImpl, ERROR_CODES } from "./errors.js";
2
+ export function ok(value) {
3
+ return { ok: true, value };
4
+ }
5
+ export function err(error) {
6
+ return { ok: false, error };
7
+ }
8
+ export function isOk(r) {
9
+ return r.ok === true;
10
+ }
11
+ export function isErr(r) {
12
+ return r.ok === false;
13
+ }
14
+ export function unwrap(r) {
15
+ if (r.ok)
16
+ return r.value;
17
+ throw r.error instanceof Error ? r.error : new Error(JSON.stringify(r.error));
18
+ }
19
+ export function map(r, fn) {
20
+ return r.ok ? ok(fn(r.value)) : r;
21
+ }
22
+ export function flatMap(r, fn) {
23
+ return r.ok ? fn(r.value) : r;
24
+ }
25
+ export async function tryAsync(fn) {
26
+ try {
27
+ return ok(await fn());
28
+ }
29
+ catch (e) {
30
+ return err(new AppErrorImpl(ERROR_CODES.UNKNOWN, e instanceof Error ? e.message : String(e), { cause: e }));
31
+ }
32
+ }
33
+ export function trySync(fn) {
34
+ try {
35
+ return ok(fn());
36
+ }
37
+ catch (e) {
38
+ return err(new AppErrorImpl(ERROR_CODES.UNKNOWN, e instanceof Error ? e.message : String(e), { cause: e }));
39
+ }
40
+ }
@@ -0,0 +1,11 @@
1
+ export declare function toKebabCase(input: string): string;
2
+ export declare function toPascalCase(input: string): string;
3
+ export declare function toCamelCase(input: string): string;
4
+ export declare function toSnakeCase(input: string): string;
5
+ export declare function indent(text: string, spaces?: number): string;
6
+ export declare function trimIndent(text: string): string;
7
+ export declare function truncate(text: string, max: number, suffix?: string): string;
8
+ export declare function singleLine(text: string): string;
9
+ export declare function pluralize(count: number, singular: string, plural?: string): string;
10
+ export declare function safeStringify(value: unknown, indentSpaces?: number): string;
11
+ //# sourceMappingURL=string-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string-utils.d.ts","sourceRoot":"","sources":["../../src/string/string-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,MAAM,CAMvD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,MAAM,CAGxE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,SAAI,GAAG,MAAM,CAgBtE"}
@@ -0,0 +1,69 @@
1
+ export function toKebabCase(input) {
2
+ return input
3
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
4
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
5
+ .replace(/[_\s]+/g, '-')
6
+ .toLowerCase()
7
+ .replace(/^-+|-+$/g, '');
8
+ }
9
+ export function toPascalCase(input) {
10
+ const cleaned = input.replace(/[-_\s]+/g, ' ').trim();
11
+ return cleaned
12
+ .split(' ')
13
+ .filter(Boolean)
14
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
15
+ .join('');
16
+ }
17
+ export function toCamelCase(input) {
18
+ const pascal = toPascalCase(input);
19
+ return pascal.length === 0 ? pascal : pascal.charAt(0).toLowerCase() + pascal.slice(1);
20
+ }
21
+ export function toSnakeCase(input) {
22
+ return toKebabCase(input).replace(/-/g, '_');
23
+ }
24
+ export function indent(text, spaces = 2) {
25
+ const pad = ' '.repeat(spaces);
26
+ return text
27
+ .split('\n')
28
+ .map((line) => (line.length > 0 ? pad + line : line))
29
+ .join('\n');
30
+ }
31
+ export function trimIndent(text) {
32
+ const lines = text.split('\n');
33
+ while (lines.length > 0 && lines[0].trim() === '')
34
+ lines.shift();
35
+ while (lines.length > 0 && lines[lines.length - 1].trim() === '')
36
+ lines.pop();
37
+ const indents = lines
38
+ .filter((l) => l.trim().length > 0)
39
+ .map((l) => l.match(/^(\s*)/)?.[1]?.length ?? 0);
40
+ const minIndent = indents.length > 0 ? Math.min(...indents) : 0;
41
+ if (minIndent === 0)
42
+ return lines.join('\n');
43
+ return lines.map((l) => l.slice(minIndent)).join('\n');
44
+ }
45
+ export function truncate(text, max, suffix = '…') {
46
+ if (text.length <= max)
47
+ return text;
48
+ return text.slice(0, Math.max(0, max - suffix.length)) + suffix;
49
+ }
50
+ export function singleLine(text) {
51
+ return text.replace(/\s+/g, ' ').trim();
52
+ }
53
+ export function pluralize(count, singular, plural) {
54
+ return count === 1 ? singular : (plural ?? singular + 's');
55
+ }
56
+ export function safeStringify(value, indentSpaces = 2) {
57
+ const seen = new WeakSet();
58
+ return JSON.stringify(value, (_key, val) => {
59
+ if (typeof val === 'object' && val !== null) {
60
+ if (seen.has(val))
61
+ return '[Circular]';
62
+ seen.add(val);
63
+ }
64
+ if (val instanceof Error) {
65
+ return { name: val.name, message: val.message, stack: val.stack };
66
+ }
67
+ return val;
68
+ }, indentSpaces);
69
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@shrkcrft/core",
3
+ "version": "0.1.0-alpha.2",
4
+ "description": "SharkCraft core primitives: Result, AppError, logger, file-system abstraction, path/string/object utils, IDs.",
5
+ "license": "MIT",
6
+ "author": "SharkCraft contributors",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/shrkcrft/sharkcraft.git",
25
+ "directory": "packages/core"
26
+ },
27
+ "homepage": "https://github.com/shrkcrft/sharkcraft",
28
+ "bugs": {
29
+ "url": "https://github.com/shrkcrft/sharkcraft/issues"
30
+ },
31
+ "keywords": [
32
+ "sharkcraft",
33
+ "core",
34
+ "result",
35
+ "error",
36
+ "logger",
37
+ "path"
38
+ ],
39
+ "engines": {
40
+ "bun": ">=1.1.0",
41
+ "node": ">=18"
42
+ },
43
+ "scripts": {
44
+ "typecheck": "tsc --noEmit -p tsconfig.json"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
49
+ }