akanjs 3.0.0-alpha.45 → 3.0.0-alpha.46

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.
@@ -209,12 +209,12 @@ declare global {
209
209
  [DEFAULT_VALUE]: boolean;
210
210
  [PURIFIED_VALUE]: boolean;
211
211
  [EXAMPLE_VALUE]: boolean;
212
- validate(value: boolean | number): boolean;
213
- parseValue(input: boolean | number): boolean | number;
214
- serializeValue(value: boolean | number): boolean | number;
215
- _parse(input: boolean | number): boolean;
216
- _serialize(value: boolean | number): boolean;
217
- _checkValue(value: boolean | number): void;
212
+ validate(value: boolean | number | string): boolean;
213
+ parseValue(input: boolean | number | string): boolean | number | string;
214
+ serializeValue(value: boolean | number | string): boolean | number | string;
215
+ _parse(input: boolean | number | string): boolean;
216
+ _serialize(value: boolean | number | string): boolean;
217
+ _checkValue(value: boolean | number | string): void;
218
218
  }
219
219
  interface DateConstructor {
220
220
  refName: "Date";
@@ -305,10 +305,14 @@ Object.assign(String, scalarPrimitiveStatics, {
305
305
  });
306
306
  PrimitiveRegistry.register(String);
307
307
 
308
- const normalizeBooleanPrimitiveValue = (value: boolean | number): boolean | null => {
308
+ const normalizeBooleanPrimitiveValue = (value: boolean | number | string): boolean | null => {
309
309
  if (typeof value === "boolean") return value;
310
310
  if (value === 1) return true;
311
311
  if (value === 0) return false;
312
+ if (typeof value !== "string") return null;
313
+ const text = value.trim().toLowerCase();
314
+ if (text === "true" || text === "1") return true;
315
+ if (text === "false" || text === "0") return false;
312
316
  return null;
313
317
  };
314
318
 
@@ -317,13 +321,13 @@ Object.assign(Boolean, {
317
321
  refName: "Boolean",
318
322
  [DEFAULT_VALUE]: false,
319
323
  [EXAMPLE_VALUE]: true,
320
- validate(value: boolean | number) {
324
+ validate(value: boolean | number | string) {
321
325
  return normalizeBooleanPrimitiveValue(value) !== null;
322
326
  },
323
- parseValue(input: boolean | number) {
327
+ parseValue(input: boolean | number | string) {
324
328
  return normalizeBooleanPrimitiveValue(input) ?? input;
325
329
  },
326
- serializeValue(value: boolean | number) {
330
+ serializeValue(value: boolean | number | string) {
327
331
  return normalizeBooleanPrimitiveValue(value) ?? value;
328
332
  },
329
333
  });
package/common/index.ts CHANGED
@@ -45,6 +45,7 @@ export { randomPicks } from "./randomPicks";
45
45
  export {
46
46
  assertUniqueRoutePatterns,
47
47
  compareRouteSpecificity,
48
+ getPageSourceFileViolation,
48
49
  getRouteExports,
49
50
  isRouteSourceFile,
50
51
  isSpecialRouteLeaf,
@@ -67,27 +67,37 @@ export function isRouteSourceFile(filePath: string): boolean {
67
67
  return tryParseRouteModuleKey(key) !== null;
68
68
  }
69
69
 
70
- export function validatePageSourceFile(filePath: string, options: ValidatePageSourceFileOptions = {}): boolean {
71
- if (!SOURCE_EXT_RE.test(filePath)) return false;
70
+ /**
71
+ * Why a `page/` file breaks the route convention, or null when it is fine. Null also covers a non-source
72
+ * asset, which `page/` tolerates. `akan sync <lib>` reports these alongside its other layout violations,
73
+ * so the rule stays in one place instead of being restated where it cannot afford to throw.
74
+ */
75
+ export function getPageSourceFileViolation(filePath: string): string | null {
76
+ if (!SOURCE_EXT_RE.test(filePath)) return null;
72
77
 
73
78
  const key = filePath.startsWith("./") ? filePath : `./${filePath.split(/[\\/]/).join("/")}`;
74
79
  const match = ROUTE_SOURCE_RE.exec(key);
75
- const displayPath = options.filePath ?? key;
76
- if (!match) throw new Error(`[route-convention] invalid page source file: ${displayPath}`);
80
+ if (!match) return "invalid page source file";
77
81
 
78
82
  const file = match[1] as string;
79
83
  const ext = match[2] as string;
80
84
  const leaf = file.split("/").filter(Boolean).at(-1);
81
- if (!leaf) throw new Error(`[route-convention] invalid page source file: ${displayPath}`);
85
+ if (!leaf) return "invalid page source file";
82
86
 
83
- if (ext !== "tsx") throw new Error(`[route-convention] route source files under page/ must use .tsx: ${displayPath}`);
87
+ if (ext !== "tsx") return "route source files under page/ must use .tsx";
84
88
  if (leaf.startsWith("_") && !RESERVED_ROUTE_FILES.has(leaf) && leaf !== INTERNAL_ROOT_LAYOUT_LEAF)
85
- throw new Error(
86
- `[route-convention] only _index.tsx, _layout.tsx and _overrides.tsx are allowed as reserved route files under page/: ${displayPath}`,
87
- );
88
- if (/^[A-Z]/.test(leaf))
89
- throw new Error(`[route-convention] route page filenames must not start with an uppercase letter: ${displayPath}`);
90
- return true;
89
+ return "only _index.tsx, _layout.tsx and _overrides.tsx are allowed as reserved route files under page/";
90
+ if (/^[A-Z]/.test(leaf)) return "route page filenames must not start with an uppercase letter";
91
+ return null;
92
+ }
93
+
94
+ export function validatePageSourceFile(filePath: string, options: ValidatePageSourceFileOptions = {}): boolean {
95
+ if (!SOURCE_EXT_RE.test(filePath)) return false;
96
+
97
+ const violation = getPageSourceFileViolation(filePath);
98
+ if (!violation) return true;
99
+ const key = filePath.startsWith("./") ? filePath : `./${filePath.split(/[\\/]/).join("/")}`;
100
+ throw new Error(`[route-convention] ${violation}: ${options.filePath ?? key}`);
91
101
  }
92
102
 
93
103
  export function validateSubRoutePageKey(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "3.0.0-alpha.45",
3
+ "version": "3.0.0-alpha.46",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/service/adapt.ts CHANGED
@@ -4,8 +4,8 @@ import { type ExtractInjectInfoObject, type InjectBuilder, type InjectInfo, inje
4
4
 
5
5
  export interface Adaptor {
6
6
  readonly logger: Logger;
7
- onInit(): Promise<void>;
8
- onDestroy(): Promise<void>;
7
+ onInit(): Promise<void> | void;
8
+ onDestroy(): Promise<void> | void;
9
9
  }
10
10
 
11
11
  export type AdaptorCls<
@@ -30,9 +30,9 @@ export function adapt(name: string, injectBuilder?: InjectBuilder) {
30
30
  readonly logger = new Logger(name);
31
31
  static readonly [INJECT_META] = injectInfoMap;
32
32
  static readonly refName = name;
33
- async onInit() {
33
+ onInit(): Promise<void> | void {
34
34
  }
35
- async onDestroy() {
35
+ onDestroy(): Promise<void> | void {
36
36
  }
37
37
  }
38
38
  return Adaptor;
package/service/serve.ts CHANGED
@@ -43,9 +43,9 @@ const avoidKeys = new Set([
43
43
  export interface Service {
44
44
  readonly logger: Logger;
45
45
 
46
- onInit(): Promise<void>;
46
+ onInit(): Promise<void> | void;
47
47
  _libsOnInit(): Promise<void>;
48
- onDestroy(): Promise<void>;
48
+ onDestroy(): Promise<void> | void;
49
49
  _libsOnDestroy(): Promise<void>;
50
50
  }
51
51
 
@@ -121,9 +121,9 @@ export function serve(
121
121
  }
122
122
  static [INJECT_META] = {};
123
123
  readonly logger = new Logger(this.constructor.name);
124
- async onInit() {
124
+ onInit(): Promise<void> | void {
125
125
  }
126
- async onDestroy() {
126
+ onDestroy(): Promise<void> | void {
127
127
  }
128
128
  };
129
129
  applyMixins(srvRef, extSrvs, avoidKeys);
@@ -132,10 +132,11 @@ export function serve(
132
132
  const onDestroyFns = extSrvs.map((srv) => srv.prototype.onDestroy);
133
133
  Object.assign(srvRef.prototype, {
134
134
  async _libsOnInit(this: Service) {
135
- await Promise.all([...onInitFns.map((onInit) => onInit?.call(this)), this.onInit()]);
135
+
136
+ await Promise.all([...onInitFns, this.onInit].map(async (onInit) => await onInit?.call(this)));
136
137
  },
137
138
  async _libsOnDestroy(this: Service) {
138
- await Promise.all([...onDestroyFns.map((onDestroy) => onDestroy?.call(this)), this.onDestroy()]);
139
+ await Promise.all([...onDestroyFns, this.onDestroy].map(async (onDestroy) => await onDestroy?.call(this)));
139
140
  },
140
141
  });
141
142
 
@@ -7,8 +7,8 @@ export type InterceptorCls<Methods = {}, InjectMap extends { [key: string]: Inje
7
7
  Methods &
8
8
  ExtractInjectInfoObject<InjectMap> & {
9
9
  readonly logger: Logger;
10
- onInit(): Promise<void>;
11
- onDestroy(): Promise<void>;
10
+ onInit(): Promise<void> | void;
11
+ onDestroy(): Promise<void> | void;
12
12
  intercept(context: SignalContext): AsyncGenerator<unknown> | Promise<unknown>;
13
13
  },
14
14
  { readonly [INJECT_META]: InjectMap; readonly refName: string }
@@ -31,9 +31,9 @@ export function intercept(refName: string, injectBuilder?: InjectBuilder) {
31
31
  intercept(context: SignalContext): AsyncGenerator | Promise<(res: Response) => Promise<Response>> {
32
32
  return Promise.resolve((res: Response) => Promise.resolve(res));
33
33
  }
34
- async onInit() {
34
+ onInit(): Promise<void> | void {
35
35
  }
36
- async onDestroy() {
36
+ onDestroy(): Promise<void> | void {
37
37
  }
38
38
  };
39
39
  }
@@ -114,12 +114,12 @@ declare global {
114
114
  [DEFAULT_VALUE]: boolean;
115
115
  [PURIFIED_VALUE]: boolean;
116
116
  [EXAMPLE_VALUE]: boolean;
117
- validate(value: boolean | number): boolean;
118
- parseValue(input: boolean | number): boolean | number;
119
- serializeValue(value: boolean | number): boolean | number;
120
- _parse(input: boolean | number): boolean;
121
- _serialize(value: boolean | number): boolean;
122
- _checkValue(value: boolean | number): void;
117
+ validate(value: boolean | number | string): boolean;
118
+ parseValue(input: boolean | number | string): boolean | number | string;
119
+ serializeValue(value: boolean | number | string): boolean | number | string;
120
+ _parse(input: boolean | number | string): boolean;
121
+ _serialize(value: boolean | number | string): boolean;
122
+ _checkValue(value: boolean | number | string): void;
123
123
  }
124
124
  interface DateConstructor {
125
125
  refName: "Date";
@@ -24,7 +24,7 @@ export { pathGet } from "./pathGet.d.ts";
24
24
  export { pathSet } from "./pathSet.d.ts";
25
25
  export { randomPick } from "./randomPick.d.ts";
26
26
  export { randomPicks } from "./randomPicks.d.ts";
27
- export { assertUniqueRoutePatterns, compareRouteSpecificity, getRouteExports, isRouteSourceFile, isSpecialRouteLeaf, LAYOUT_ROUTE_EXPORTS, matchRoutePattern, normalizeRoutePattern, PAGE_ROUTE_EXPORTS, type ParsedRouteModuleKey, parseRouteModuleKey, RESERVED_ROUTE_CONFIG_EXPORTS, ROOT_LAYOUT_ROUTE_EXPORTS, type RouteModuleKind, routeSegmentToPatternPart, routeSegmentToTreePath, tryParseRouteModuleKey, type ValidatePageSourceFileOptions, type ValidateSubRoutePageKeyOptions, validatePageSourceFile, validateSubRoutePageKey, } from "./routeConvention.d.ts";
27
+ export { assertUniqueRoutePatterns, compareRouteSpecificity, getPageSourceFileViolation, getRouteExports, isRouteSourceFile, isSpecialRouteLeaf, LAYOUT_ROUTE_EXPORTS, matchRoutePattern, normalizeRoutePattern, PAGE_ROUTE_EXPORTS, type ParsedRouteModuleKey, parseRouteModuleKey, RESERVED_ROUTE_CONFIG_EXPORTS, ROOT_LAYOUT_ROUTE_EXPORTS, type RouteModuleKind, routeSegmentToPatternPart, routeSegmentToTreePath, tryParseRouteModuleKey, type ValidatePageSourceFileOptions, type ValidateSubRoutePageKeyOptions, validatePageSourceFile, validateSubRoutePageKey, } from "./routeConvention.d.ts";
28
28
  export { sleep } from "./sleep.d.ts";
29
29
  export { splitVersion } from "./splitVersion.d.ts";
30
30
  export { getBasePathFromPathname, parseBasePaths, parseSubRouteHosts, resolveSubRouteHosts } from "./subRoute.d.ts";
@@ -27,6 +27,12 @@ export interface ValidatePageSourceFileOptions {
27
27
  filePath?: string;
28
28
  }
29
29
  export declare function isRouteSourceFile(filePath: string): boolean;
30
+ /**
31
+ * Why a `page/` file breaks the route convention, or null when it is fine. Null also covers a non-source
32
+ * asset, which `page/` tolerates. `akan sync <lib>` reports these alongside its other layout violations,
33
+ * so the rule stays in one place instead of being restated where it cannot afford to throw.
34
+ */
35
+ export declare function getPageSourceFileViolation(filePath: string): string | null;
30
36
  export declare function validatePageSourceFile(filePath: string, options?: ValidatePageSourceFileOptions): boolean;
31
37
  export declare function validateSubRoutePageKey(key: string, basePaths: Iterable<string>, options?: ValidateSubRoutePageKeyOptions): void;
32
38
  export declare function parseRouteModuleKey(key: string): ParsedRouteModuleKey;
@@ -3,8 +3,8 @@ import { Logger } from "akanjs/common";
3
3
  import { type ExtractInjectInfoObject, type InjectBuilder, type InjectInfo } from "./injectInfo.d.ts";
4
4
  export interface Adaptor {
5
5
  readonly logger: Logger;
6
- onInit(): Promise<void>;
7
- onDestroy(): Promise<void>;
6
+ onInit(): Promise<void> | void;
7
+ onDestroy(): Promise<void> | void;
8
8
  }
9
9
  export type AdaptorCls<Methods = any, InjectMap extends Record<string, InjectInfo> = {}> = Cls<Methods & ExtractInjectInfoObject<InjectMap> & Adaptor, {
10
10
  readonly [INJECT_META]: InjectMap;
@@ -10,9 +10,9 @@ interface ServiceOptions {
10
10
  export type ServiceType = "database" | "plain";
11
11
  export interface Service {
12
12
  readonly logger: Logger;
13
- onInit(): Promise<void>;
13
+ onInit(): Promise<void> | void;
14
14
  _libsOnInit(): Promise<void>;
15
- onDestroy(): Promise<void>;
15
+ onDestroy(): Promise<void> | void;
16
16
  _libsOnDestroy(): Promise<void>;
17
17
  }
18
18
  export type ServiceCls<RefName extends string = string, Methods = {}, InjectMap extends {
@@ -6,8 +6,8 @@ export type InterceptorCls<Methods = {}, InjectMap extends {
6
6
  [key: string]: InjectInfo;
7
7
  } = {}> = Cls<Methods & ExtractInjectInfoObject<InjectMap> & {
8
8
  readonly logger: Logger;
9
- onInit(): Promise<void>;
10
- onDestroy(): Promise<void>;
9
+ onInit(): Promise<void> | void;
10
+ onDestroy(): Promise<void> | void;
11
11
  intercept(context: SignalContext): AsyncGenerator<unknown> | Promise<unknown>;
12
12
  }, {
13
13
  readonly [INJECT_META]: InjectMap;