jopi-toolkit 3.0.15 → 3.1.22

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 (56) hide show
  1. package/dist/jk_app/common.d.ts +8 -5
  2. package/dist/jk_app/common.js +35 -19
  3. package/dist/jk_app/common.js.map +1 -1
  4. package/dist/jk_data/index.d.ts +32 -17
  5. package/dist/jk_data/index.js +55 -25
  6. package/dist/jk_data/index.js.map +1 -1
  7. package/dist/jk_events/index.d.ts +2 -0
  8. package/dist/jk_events/index.js +29 -7
  9. package/dist/jk_events/index.js.map +1 -1
  10. package/dist/jk_fs/jBundler_ifBrowser.d.ts +2 -0
  11. package/dist/jk_fs/jBundler_ifBrowser.js +6 -0
  12. package/dist/jk_fs/jBundler_ifBrowser.js.map +1 -1
  13. package/dist/jk_fs/jBundler_ifServer.d.ts +13 -11
  14. package/dist/jk_fs/jBundler_ifServer.js +53 -7
  15. package/dist/jk_fs/jBundler_ifServer.js.map +1 -1
  16. package/dist/jk_logs/jBundler_ifServer.js +5 -0
  17. package/dist/jk_logs/jBundler_ifServer.js.map +1 -1
  18. package/dist/jk_process/jBundler_ifServer.d.ts +4 -0
  19. package/dist/jk_process/jBundler_ifServer.js +31 -0
  20. package/dist/jk_process/jBundler_ifServer.js.map +1 -1
  21. package/dist/jk_schemas/index.d.ts +82 -6
  22. package/dist/jk_schemas/index.js +48 -5
  23. package/dist/jk_schemas/index.js.map +1 -1
  24. package/dist/jk_term/index.d.ts +2 -0
  25. package/dist/jk_term/index.js +2 -0
  26. package/dist/jk_term/index.js.map +1 -1
  27. package/dist/jk_tools/jBundler_ifServer.js +19 -7
  28. package/dist/jk_tools/jBundler_ifServer.js.map +1 -1
  29. package/package.json +26 -18
  30. package/src/jk_app/common.js +40 -24
  31. package/src/jk_app/common.ts +41 -26
  32. package/src/jk_data/index.js +68 -30
  33. package/src/jk_data/index.ts +86 -49
  34. package/src/jk_events/index.js +15 -7
  35. package/src/jk_events/index.ts +50 -19
  36. package/src/jk_fs/jBundler_ifBrowser.ts +8 -0
  37. package/src/jk_fs/jBundler_ifServer.ts +54 -7
  38. package/src/jk_logs/jBundler_ifServer.ts +6 -0
  39. package/src/{jk_fs → jk_process}/index.js +0 -1
  40. package/src/jk_process/jBundler_ifServer.ts +35 -1
  41. package/src/jk_schemas/index.js +50 -5
  42. package/src/jk_schemas/index.ts +140 -11
  43. package/src/jk_term/index.js +2 -0
  44. package/src/jk_term/index.ts +3 -0
  45. package/src/jk_tools/jBundler_ifServer.ts +19 -6
  46. package/src/jk_what/jBundler_ifServer.js +8 -5
  47. package/dist/jk_schemas/jkSchemas.d.ts +0 -188
  48. package/dist/jk_schemas/jkSchemas.js +0 -257
  49. package/dist/jk_schemas/jkSchemas.js.map +0 -1
  50. package/dist/jk_translate/index.d.ts +0 -21
  51. package/dist/jk_translate/index.js +0 -56
  52. package/dist/jk_translate/index.js.map +0 -1
  53. package/src/jk_fs/common.js +0 -1
  54. package/src/jk_fs/jBundler_ifServer.js +0 -729
  55. package/src/jk_translate/index.js +0 -56
  56. package/src/jk_translate/index.ts +0 -85
@@ -57,15 +57,21 @@ var EventGroup = /** @class */ (function () {
57
57
  if (this.evenSpy)
58
58
  this.evenSpy(eventName, e);
59
59
  var events = this.listenersFor[eventName];
60
- if (!events)
61
- return;
62
- if (events.value) {
63
- var values = events.value;
64
- for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
65
- var listener = values_1[_i];
66
- listener(e, eventName);
60
+ //
61
+ if (events) {
62
+ if (events.value) {
63
+ var values = events.value;
64
+ for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
65
+ var listener = values_1[_i];
66
+ listener(e, eventName);
67
+ }
67
68
  }
68
69
  }
70
+ var staticEvent = gStaticEvents[eventName];
71
+ //
72
+ if (staticEvent) {
73
+ staticEvent.send(e);
74
+ }
69
75
  };
70
76
  EventGroup.prototype.sendAsyncEvent = function (eventName, e) {
71
77
  return __awaiter(this, void 0, void 0, function () {
@@ -178,6 +184,7 @@ var StaticEventImpl = /** @class */ (function () {
178
184
  function StaticEventImpl(eventName, eventItems) {
179
185
  this.eventName = eventName;
180
186
  this.eventItems = eventItems;
187
+ gStaticEvents[eventName] = this;
181
188
  }
182
189
  StaticEventImpl.prototype.send = function (data) {
183
190
  for (var _i = 0, _a = this.eventItems; _i < _a.length; _i++) {
@@ -211,3 +218,4 @@ export var sendAsyncEvent = defaultEventGroup.sendAsyncEvent.bind(defaultEventGr
211
218
  export function addListener(eventName, priorityOrListener, listener) {
212
219
  defaultEventGroup.addListener(eventName, priorityOrListener, listener);
213
220
  }
221
+ var gStaticEvents = {};
@@ -1,14 +1,19 @@
1
- import {PriorityLevel as EventPriority} from "jopi-toolkit/jk_tools";
1
+ import { PriorityLevel as EventPriority } from "jopi-toolkit/jk_tools";
2
+ import { isBrowser } from "jopi-toolkit/jk_what";
3
+
2
4
  // Warning: it's export.
3
- export {PriorityLevel as EventPriority} from "jopi-toolkit/jk_tools";
5
+ export { PriorityLevel as EventPriority } from "jopi-toolkit/jk_tools";
4
6
 
5
7
  // noinspection JSUnusedGlobalSymbols
6
8
 
7
- export type EventListener<T = any> = (e: T, eventName: string) => void|Promise<void>;
9
+ export type EventListener<T = any> = (e: T, eventName: string) => void | Promise<void>;
8
10
  export type SyncEventListener<T = any> = (e: T, eventName: string) => void;
9
11
 
10
12
  export type EventListenerProvider = () => Promise<EventListener[]>;
11
13
 
14
+ let gStaticEvents: Record<string, any> = {};
15
+ let gStaticEventsThisValue: any;
16
+
12
17
  export class EventGroup {
13
18
  private readonly listenersFor: Record<string, PriorityArray<EventListener>> = {};
14
19
  private evenSpy: undefined | ((eventName: string, data?: any) => void);
@@ -27,23 +32,30 @@ export class EventGroup {
27
32
  if (events) events.remove(listener);
28
33
  }
29
34
 
30
- sendEvent(eventName: string, e?: any|undefined): void {
35
+ sendEvent(eventName: string, e?: any | undefined): void {
31
36
  if (this.evenSpy) this.evenSpy(eventName, e);
32
37
 
33
38
  const events = this.listenersFor[eventName];
34
- if (!events) return;
35
-
36
- if (events.value) {
37
- const values = events.value;
38
-
39
- for (const listener of values) {
40
- listener(e, eventName);
39
+ //
40
+ if (events) {
41
+ if (events.value) {
42
+ const values = events.value;
43
+
44
+ for (const listener of values) {
45
+ listener(e, eventName);
46
+ }
41
47
  }
42
48
  }
49
+
50
+ let staticEvent = gStaticEvents[eventName];
51
+ //
52
+ if (staticEvent) {
53
+ staticEvent.send(e);
54
+ }
43
55
  }
44
56
 
45
- async sendAsyncEvent(eventName: string, e?: any|undefined): Promise<void> {
46
- if (eventName[0]!=='@') {
57
+ async sendAsyncEvent(eventName: string, e?: any | undefined): Promise<void> {
58
+ if (eventName[0] !== '@') {
47
59
  throw new Error(`Async events ${eventName} must start with @`);
48
60
  }
49
61
 
@@ -74,7 +86,7 @@ export class EventGroup {
74
86
  }
75
87
  }
76
88
 
77
- addListener<T = any|undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
89
+ addListener<T = any | undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
78
90
  let priority: EventPriority;
79
91
  let actualListener: EventListener;
80
92
 
@@ -109,11 +121,11 @@ export class EventGroup {
109
121
  */
110
122
  class PriorityArray<T> {
111
123
  private entries: PriorityArrayEntry<T>[] = [];
112
- private build: T[]|undefined;
124
+ private build: T[] | undefined;
113
125
 
114
126
  add(priority: EventPriority, value: T) {
115
127
  this.build = undefined;
116
- this.entries.push({priority, value});
128
+ this.entries.push({ priority, value });
117
129
  }
118
130
 
119
131
  remove(value: T) {
@@ -127,7 +139,7 @@ class PriorityArray<T> {
127
139
  }
128
140
 
129
141
  return this.build = this.entries
130
- .sort((a,b) => Number(a.priority) - Number(b.priority))
142
+ .sort((a, b) => Number(a.priority) - Number(b.priority))
131
143
  .map(e => e.value);
132
144
  }
133
145
  }
@@ -141,6 +153,7 @@ interface PriorityArrayEntry<T> {
141
153
 
142
154
  export interface StaticEvent {
143
155
  send<T>(data: T): T;
156
+ setThisValue(value: any): void;
144
157
  }
145
158
 
146
159
  export interface SEventController {
@@ -148,12 +161,17 @@ export interface SEventController {
148
161
  }
149
162
 
150
163
  class StaticEventImpl implements StaticEvent, SEventController {
164
+ private thisValue: any;
165
+
151
166
  constructor(public readonly eventName: string, private readonly eventItems: SyncEventListener[]) {
167
+ gStaticEvents[eventName] = this;
152
168
  }
153
169
 
154
170
  send<T>(data: T): T {
171
+ let thisValue = this.thisValue ?? gStaticEventsThisValue;
172
+
155
173
  for (const listener of this.eventItems) {
156
- listener(data, this.eventName);
174
+ listener.call(thisValue, data, this.eventName);
157
175
  }
158
176
 
159
177
  return data;
@@ -167,12 +185,25 @@ class StaticEventImpl implements StaticEvent, SEventController {
167
185
  if (idx >= 0) this.eventItems.splice(idx, 1);
168
186
  };
169
187
  }
188
+
189
+ setThisValue(thisValue: any): void {
190
+ if (isBrowser) {
191
+ this.thisValue = thisValue;
192
+ }
193
+ }
170
194
  }
171
195
 
172
196
  export function createStaticEvent(eventName: string, eventItems: SyncEventListener[]): StaticEvent {
173
197
  return new StaticEventImpl(eventName, eventItems);
174
198
  }
175
199
 
200
+ export function setStaticEventsThisValue(thisValue: any) {
201
+ if (isBrowser) {
202
+ gStaticEventsThisValue = thisValue;
203
+ }
204
+ }
205
+
206
+
176
207
  export const defaultEventGroup = new EventGroup();
177
208
 
178
209
  export function newEventGroup(): EventGroup {
@@ -184,6 +215,6 @@ export const removeListener = defaultEventGroup.removeListener.bind(defaultEvent
184
215
  export const sendEvent = defaultEventGroup.sendEvent.bind(defaultEventGroup);
185
216
  export const sendAsyncEvent = defaultEventGroup.sendAsyncEvent.bind(defaultEventGroup);
186
217
 
187
- export function addListener<T = any|undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
218
+ export function addListener<T = any | undefined>(eventName: string, priorityOrListener: EventPriority | EventListener<T>, listener?: EventListener<T>): void {
188
219
  defaultEventGroup.addListener(eventName, priorityOrListener as EventPriority, listener as EventListener<T>);
189
220
  }
@@ -68,6 +68,14 @@ export function readTextFromFileSync(filePath: string): string {
68
68
  throw new Error("Not implemented");
69
69
  }
70
70
 
71
+ export async function readJsonFromFile<T = any>(filePath: string, throwError: boolean = false): Promise<T> {
72
+ throw new Error("Not implemented");
73
+ }
74
+
75
+ export function readJsonFromFileSync<T = any>(filePath: string, throwError: boolean = false): T {
76
+ throw new Error("Not implemented");
77
+ }
78
+
71
79
  export async function isFile(filePath: string): Promise<boolean> {
72
80
  throw new Error("Not implemented");
73
81
  }
@@ -181,17 +181,64 @@ export function writeTextToFileSync(filePath: string, text: string, createDir: b
181
181
  fss.writeFileSync(filePath, text, {encoding: 'utf8', flag: 'w'});
182
182
  }
183
183
 
184
- export function readTextFromFile(filePath: string): Promise<string> {
185
- return fs.readFile(filePath, 'utf8');
184
+ export async function readTextFromFile(filePath: string, throwError: boolean = false): Promise<string> {
185
+ if (throwError) {
186
+ return fs.readFile(filePath, 'utf8');
187
+ }
188
+
189
+ try {
190
+ return await fs.readFile(filePath, 'utf8');
191
+ }
192
+ catch {
193
+ // @ts-ignore
194
+ return undefined;
195
+ }
196
+ }
197
+
198
+ export function readTextFromFileSync(filePath: string, throwError: boolean = false): string {
199
+ if (throwError) {
200
+ return fss.readFileSync(filePath, 'utf8');
201
+ }
202
+
203
+ try {
204
+ return fss.readFileSync(filePath, 'utf8');
205
+ }
206
+ catch {
207
+ // @ts-ignore
208
+ return undefined;
209
+ }
186
210
  }
187
211
 
188
- export async function readJsonFromFile<T = any>(filePath: string): Promise<T> {
189
- let txt = await fs.readFile(filePath, 'utf8');
190
- return JSON.parse(txt) as T;
212
+ export async function readJsonFromFile<T = any>(filePath: string, throwError: boolean = false): Promise<T> {
213
+ if (throwError) {
214
+ let txt = await readTextFromFile(filePath, true);
215
+ return JSON.parse(txt) as T;
216
+ } else {
217
+ try {
218
+ let txt = await readTextFromFile(filePath);
219
+ return JSON.parse(txt) as T;
220
+ }
221
+ catch {
222
+ // @ts-ignore
223
+ return undefined;
224
+ }
225
+ }
191
226
  }
192
227
 
193
- export function readTextFromFileSync(filePath: string): string {
194
- return fss.readFileSync(filePath, 'utf8');
228
+ export function readJsonFromFileSync<T = any>(filePath: string, throwError: boolean = false): T {
229
+ if (throwError) {
230
+ let txt = readTextFromFileSync(filePath);
231
+ return JSON.parse(txt) as T;
232
+ } else {
233
+ try {
234
+ let txt = readTextFromFileSync(filePath);
235
+ return JSON.parse(txt) as T;
236
+ }
237
+ catch {
238
+ // @ts-ignore
239
+ return undefined;
240
+ }
241
+ }
195
242
  }
196
243
 
197
244
  export async function isFile(filePath: string): Promise<boolean> {
@@ -4,6 +4,12 @@ import type {LogInitializer} from "./index.ts";
4
4
 
5
5
  export function init(init: LogInitializer) {
6
6
  const mainDir = jk_app.findPackageJsonDir();
7
+
8
+ // Possible that no package.json is found.
9
+ // For exemple, when using a command line tool (ex: jopi init).
10
+ //
11
+ if (!mainDir) return;
12
+
7
13
  const filePath = jk_fs.join(mainDir, "logConfig.json");
8
14
 
9
15
  if (!jk_fs.isFileSync(filePath)) return;
@@ -1,2 +1 @@
1
1
  export * from "./jBundler_ifServer.ts";
2
- export * from "./common.ts";
@@ -1,4 +1,38 @@
1
+ import * as inspector from "node:inspector";
2
+
1
3
  export const argv = process.argv;
2
4
  export const env = process.env as Record<string, string>;
3
5
  export const isProduction = process.env.NODE_ENV === 'production';
4
- export const isDevelopment: boolean = !isProduction;
6
+ export const isDevelopment: boolean = !isProduction;
7
+
8
+ /**
9
+ * Detect if Node.js or Bun.js is running with the debugger launcher.
10
+ */
11
+ export function isLaunchedWithDebugger(): boolean {
12
+ const args = process.execArgv;
13
+ if (args.some((arg) => arg.includes("--inspect") || arg.includes("--debug") || arg.includes("bootloader.js"))) return true;
14
+
15
+ // Check environment variables
16
+ if (process.env.VSCODE_INSPECTOR_OPTIONS) return true;
17
+
18
+ if (process.env.NODE_OPTIONS) {
19
+ const nodeOptions = process.env.NODE_OPTIONS;
20
+ if (nodeOptions.includes("--inspect") ||
21
+ nodeOptions.includes("--debug") ||
22
+ nodeOptions.includes("bootloader.js") ||
23
+ nodeOptions.includes("js-debug")) {
24
+ return true;
25
+ }
26
+ }
27
+
28
+ // Check Bun environment variables
29
+ if (process.env.BUN_INSPECT || process.env.BUN_INSPECT_BRK || process.env.BUN_INSPECT_WAIT) return true;
30
+
31
+
32
+ // Check inspector url. This is the most reliable way to detect if a debugger is attached.
33
+ try {
34
+ if (inspector.url()) return true;
35
+ } catch { /* ignore */ }
36
+
37
+ return false;
38
+ }
@@ -54,7 +54,7 @@ export function validateSchema(data, schema) {
54
54
  //
55
55
  if (schema.schemaMeta.normalize) {
56
56
  try {
57
- schema.schemaMeta.normalize(data);
57
+ schema.schemaMeta.normalize(data, gValueCheckingHelper);
58
58
  }
59
59
  catch (e) {
60
60
  if (e instanceof SchemaError) {
@@ -81,7 +81,7 @@ export function validateSchema(data, schema) {
81
81
  var value = data[fieldName];
82
82
  if (field.normalize) {
83
83
  defaultErrorMessage = field.errorMessage_theValueIsInvalid;
84
- field.normalize(value, data);
84
+ field.normalize(value, data, gValueCheckingHelper);
85
85
  }
86
86
  if (!field.optional) {
87
87
  if (value === undefined) {
@@ -102,7 +102,7 @@ export function validateSchema(data, schema) {
102
102
  }
103
103
  if (field.validator) {
104
104
  defaultErrorMessage = field.errorMessage_theValueIsInvalid;
105
- field.validator(value, data);
105
+ field.validator(value, data, gValueCheckingHelper);
106
106
  }
107
107
  }
108
108
  catch (e) {
@@ -124,7 +124,7 @@ export function validateSchema(data, schema) {
124
124
  // Allow validating if values are ok with each others.
125
125
  if (schema.schemaMeta.validate) {
126
126
  try {
127
- schema.schemaMeta.validate(data);
127
+ schema.schemaMeta.validate(data, gValueCheckingHelper);
128
128
  }
129
129
  catch (e) {
130
130
  if (e instanceof SchemaError) {
@@ -147,6 +147,19 @@ export function validateSchema(data, schema) {
147
147
  return { fields: fieldErrors };
148
148
  }
149
149
  var byTypeValidator = {};
150
+ /**
151
+ * A helper allowing to make field validation easier.
152
+ * Is sent to normalize and validate functions.
153
+ */
154
+ var ValueCheckingHelper = /** @class */ (function () {
155
+ function ValueCheckingHelper() {
156
+ }
157
+ ValueCheckingHelper.prototype.declareError = function (message, errorCode) {
158
+ throw new SchemaError(message, errorCode);
159
+ };
160
+ return ValueCheckingHelper;
161
+ }());
162
+ var gValueCheckingHelper = new ValueCheckingHelper();
150
163
  export function registerSchema(schemaId, schema, meta) {
151
164
  if (!schemaId) {
152
165
  throw new Error("jk_schemas - Schema id required. If you need an uid you can use: " + generateUUIDv4());
@@ -176,8 +189,40 @@ var gRegistry = {};
176
189
  //endregion
177
190
  //region Schema
178
191
  export function schema(descriptor, meta) {
179
- return { desc: descriptor, schemaMeta: meta || {} };
192
+ return new SchemaImpl(descriptor, meta || {});
180
193
  }
194
+ var SchemaImpl = /** @class */ (function () {
195
+ function SchemaImpl(desc, schemaMeta) {
196
+ this.desc = desc;
197
+ this.schemaMeta = schemaMeta;
198
+ }
199
+ SchemaImpl.prototype.toJson = function () {
200
+ return toJson(this);
201
+ };
202
+ SchemaImpl.prototype.addDataNormalizer = function (f) {
203
+ if (!this.schemaMeta.normalize) {
204
+ this.schemaMeta.normalize = f;
205
+ }
206
+ var f1 = this.schemaMeta.normalize;
207
+ this.schemaMeta.normalize = function (values, helper) {
208
+ f1(values, helper);
209
+ f(values, helper);
210
+ };
211
+ return this;
212
+ };
213
+ SchemaImpl.prototype.addDataValidator = function (f) {
214
+ if (!this.schemaMeta.validate) {
215
+ this.schemaMeta.validate = f;
216
+ }
217
+ var f1 = this.schemaMeta.validate;
218
+ this.schemaMeta.validate = function (values, helper) {
219
+ f1(values, helper);
220
+ f(values, helper);
221
+ };
222
+ return this;
223
+ };
224
+ return SchemaImpl;
225
+ }());
181
226
  export function toJson(schema) {
182
227
  return schema;
183
228
  }
@@ -47,7 +47,7 @@ export function validateSchema(data: any, schema: Schema): ValidationErrors|unde
47
47
  //
48
48
  if (schema.schemaMeta.normalize) {
49
49
  try {
50
- schema.schemaMeta.normalize(data);
50
+ schema.schemaMeta.normalize(data, gValueCheckingHelper);
51
51
  }
52
52
  catch (e: any) {
53
53
  if (e instanceof SchemaError) {
@@ -80,7 +80,7 @@ export function validateSchema(data: any, schema: Schema): ValidationErrors|unde
80
80
 
81
81
  if (field.normalize) {
82
82
  defaultErrorMessage = field.errorMessage_theValueIsInvalid;
83
- field.normalize(value, data);
83
+ field.normalize(value, data, gValueCheckingHelper);
84
84
  }
85
85
 
86
86
  if (!field.optional) {
@@ -103,7 +103,7 @@ export function validateSchema(data: any, schema: Schema): ValidationErrors|unde
103
103
 
104
104
  if (field.validator) {
105
105
  defaultErrorMessage = field.errorMessage_theValueIsInvalid;
106
- field.validator(value, data);
106
+ field.validator(value, data, gValueCheckingHelper);
107
107
  }
108
108
  }
109
109
  catch (e: any) {
@@ -126,7 +126,7 @@ export function validateSchema(data: any, schema: Schema): ValidationErrors|unde
126
126
 
127
127
  if (schema.schemaMeta.validate) {
128
128
  try {
129
- schema.schemaMeta.validate(data);
129
+ schema.schemaMeta.validate(data, gValueCheckingHelper);
130
130
  }
131
131
  catch (e: any) {
132
132
  if (e instanceof SchemaError) {
@@ -151,6 +151,18 @@ export function validateSchema(data: any, schema: Schema): ValidationErrors|unde
151
151
 
152
152
  const byTypeValidator: Record<string, (v: any, fieldInfos: SchemaFieldInfos) => void> = {};
153
153
 
154
+ /**
155
+ * A helper allowing to make field validation easier.
156
+ * Is sent to normalize and validate functions.
157
+ */
158
+ class ValueCheckingHelper {
159
+ declareError(message?: string, errorCode?: string) {
160
+ throw new SchemaError(message, errorCode);
161
+ }
162
+ }
163
+
164
+ const gValueCheckingHelper = new ValueCheckingHelper();
165
+
154
166
  //endregion
155
167
 
156
168
  //region Registry
@@ -197,11 +209,50 @@ const gRegistry: Record<string, RegistryEntry> = {};
197
209
  //region Schema
198
210
 
199
211
  export function schema<T extends SchemaDescriptor>(descriptor: T, meta?: SchemaMeta): Schema & { desc: T } {
200
- return { desc: descriptor, schemaMeta: meta || {} };
212
+ return new SchemaImpl(descriptor, meta || {});
213
+ }
214
+
215
+ class SchemaImpl<T extends SchemaDescriptor> implements Schema {
216
+ constructor(public readonly desc: T, public readonly schemaMeta: SchemaMeta) {
217
+ }
218
+
219
+ toJson(): SchemaInfo {
220
+ return toJson(this);
221
+ }
222
+
223
+ addDataNormalizer(f: (allValues: any, checkHelper: ValueCheckingHelper) => void): this {
224
+ if (!this.schemaMeta.normalize) {
225
+ this.schemaMeta.normalize = f;
226
+ }
227
+
228
+ const f1 = this.schemaMeta.normalize;
229
+
230
+ this.schemaMeta.normalize = function (values, helper) {
231
+ f1(values, helper);
232
+ f(values, helper);
233
+ }
234
+
235
+ return this;
236
+ }
237
+
238
+ addDataValidator(f: (allValues: any, checkHelper: ValueCheckingHelper) => void): this {
239
+ if (!this.schemaMeta.validate) {
240
+ this.schemaMeta.validate = f;
241
+ }
242
+
243
+ const f1 = this.schemaMeta.validate;
244
+
245
+ this.schemaMeta.validate = function (values, helper) {
246
+ f1(values, helper);
247
+ f(values, helper);
248
+ }
249
+
250
+ return this;
251
+ }
201
252
  }
202
253
 
203
254
  export interface SchemaDescriptor {
204
- [field: string]: ScField<any, any>;
255
+ [field: string]: Field;
205
256
  }
206
257
 
207
258
  export interface SchemaMeta {
@@ -209,8 +260,8 @@ export interface SchemaMeta {
209
260
  description?: string;
210
261
  [key: string]: any;
211
262
 
212
- normalize?: (allValues: any) => void;
213
- validate?: (allValues: any) => void;
263
+ normalize?: (allValues: any, checkHelper: ValueCheckingHelper) => void;
264
+ validate?: (allValues: any, checkHelper: ValueCheckingHelper) => void;
214
265
  }
215
266
 
216
267
  export interface SchemaInfo {
@@ -219,6 +270,26 @@ export interface SchemaInfo {
219
270
  }
220
271
 
221
272
  export interface Schema extends SchemaInfo {
273
+ /**
274
+ * Get serializable data describing this schema.
275
+ */
276
+ toJson(): SchemaInfo;
277
+
278
+ /**
279
+ * Add a function whose role is to normalize the data.
280
+ *
281
+ * Cumulating: if a normalize function has already been added,
282
+ * then the previous function will be called before this one.
283
+ */
284
+ addDataNormalizer(f: (allValues: any, checkHelper: ValueCheckingHelper) => void): this;
285
+
286
+ /**
287
+ * Add a function whose role is to validate the data.
288
+ *
289
+ * Cumulating: if a validate function has already been added,
290
+ * then the previous function will be called before this one.
291
+ */
292
+ addDataValidator(f: (allValues: any, checkHelper: ValueCheckingHelper) => void): this;
222
293
  }
223
294
 
224
295
  export function toJson(schema: Schema): SchemaInfo {
@@ -304,7 +375,46 @@ export interface ScOnTableRenderingInfo {
304
375
  textAlign?: "left" | "center" | "right";
305
376
  }
306
377
 
307
- interface ScField<T, Opt extends boolean> {
378
+ /**
379
+ * Get information about how storing this field.
380
+ */
381
+ export interface ScFieldStore {
382
+ /**
383
+ * Allow knowing if a BDD index must be created for this field.
384
+ * The default is true.
385
+ */
386
+ mustIndex?: boolean;
387
+
388
+ /**
389
+ * Allow knowing if this field is the primary key.
390
+ * If more than one primary is set, then a composed key will be created.
391
+ * The default is false.
392
+ */
393
+ isPrimaryKey?: boolean;
394
+
395
+ /**
396
+ * The column name to use when storing this field in a database.
397
+ * The default is the field name.
398
+ */
399
+ colName?: string;
400
+
401
+ /**
402
+ * An indication on the size of the data to store.
403
+ * The default is "default".
404
+ *
405
+ * - big: for storing large strings / binary.
406
+ * - medium: for storing item with a common size.
407
+ * - tiny: for storing small strings / binary.
408
+ */
409
+ dataSize?: "tiny" | "medium" | "big";
410
+
411
+ /**
412
+ * Allow forcing the type name for the BDD.
413
+ */
414
+ databaseType?: string;
415
+ }
416
+
417
+ export interface ScField<T, Opt extends boolean> {
308
418
  title: string;
309
419
  type: string;
310
420
 
@@ -316,12 +426,31 @@ interface ScField<T, Opt extends boolean> {
316
426
  errorMessage_theDataTypeIsInvalid?: string;
317
427
  errorMessage_theValueIsInvalid?: string;
318
428
 
319
- normalize?: (value: T, allValues: any) => void;
320
- validator?: (value: T, allValues: any) => void;
429
+ /**
430
+ * A function used to normalize the field value.
431
+ */
432
+ normalize?: (value: T, allValues: any, valueCheckingHelp: ValueCheckingHelper) => void;
433
+
434
+ /**
435
+ * A function used to validate the field.
436
+ */
437
+ validator?: (value: T, allValues: any, valueCheckingHelp: ValueCheckingHelper) => void;
321
438
 
439
+ /**
440
+ * Meta-data associated with this field.
441
+ * The usage is free, you can use it for whatever you want.
442
+ */
322
443
  metas?: Record<string, string>;
323
444
 
445
+ /**
446
+ * Get information about how to render this field in a data table.
447
+ */
324
448
  onTableRendering?: ScOnTableRenderingInfo;
449
+
450
+ /**
451
+ * Get information about how storing this field.
452
+ */
453
+ store?: ScFieldStore;
325
454
  }
326
455
 
327
456
  export type Field = ScField<any, any>;
@@ -114,6 +114,8 @@ export var logBgRed = buildLogger(B_RED);
114
114
  export var textBgRed = buildWriter(B_RED);
115
115
  export var logBlue = buildLogger(C_BLUE);
116
116
  export var textBlue = buildWriter(C_BLUE);
117
+ export var logGrey = buildLogger(C_GREY);
118
+ export var textGrey = buildWriter(C_GREY);
117
119
  export var logBgBlue = buildLogger(B_BLUE, C_WHITE);
118
120
  export var textBgBlue = buildWriter(B_BLUE, C_WHITE);
119
121
  export var logGreen = buildLogger(C_GREEN);
@@ -105,6 +105,9 @@ export const textBgRed = buildWriter(B_RED);
105
105
  export const logBlue = buildLogger(C_BLUE);
106
106
  export const textBlue = buildWriter(C_BLUE);
107
107
 
108
+ export const logGrey = buildLogger(C_GREY);
109
+ export const textGrey = buildWriter(C_GREY);
110
+
108
111
  export const logBgBlue = buildLogger(B_BLUE, C_WHITE);
109
112
  export const textBgBlue = buildWriter(B_BLUE, C_WHITE);
110
113