@hamak/shared-utils 0.4.1
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.
- package/dist/core-utils-chrono.d.ts +8 -0
- package/dist/core-utils-chrono.d.ts.map +1 -0
- package/dist/core-utils-chrono.js +16 -0
- package/dist/core-utils-comparator.d.ts +11 -0
- package/dist/core-utils-comparator.d.ts.map +1 -0
- package/dist/core-utils-comparator.js +22 -0
- package/dist/core-utils-file.d.ts +10 -0
- package/dist/core-utils-file.d.ts.map +1 -0
- package/dist/core-utils-file.js +1 -0
- package/dist/core-utils-filesystem.d.ts +69 -0
- package/dist/core-utils-filesystem.d.ts.map +1 -0
- package/dist/core-utils-filesystem.js +156 -0
- package/dist/core-utils-fqn.d.ts +11 -0
- package/dist/core-utils-fqn.d.ts.map +1 -0
- package/dist/core-utils-fqn.js +19 -0
- package/dist/core-utils-pathway-resolver.d.ts +20 -0
- package/dist/core-utils-pathway-resolver.d.ts.map +1 -0
- package/dist/core-utils-pathway-resolver.js +31 -0
- package/dist/core-utils-pathway.d.ts +84 -0
- package/dist/core-utils-pathway.d.ts.map +1 -0
- package/dist/core-utils-pathway.js +206 -0
- package/dist/core-utils-registry.d.ts +7 -0
- package/dist/core-utils-registry.d.ts.map +1 -0
- package/dist/core-utils-registry.js +22 -0
- package/dist/core-utils-resolver.d.ts +33 -0
- package/dist/core-utils-resolver.d.ts.map +1 -0
- package/dist/core-utils-resolver.js +65 -0
- package/dist/core-utils-stack.d.ts +20 -0
- package/dist/core-utils-stack.d.ts.map +1 -0
- package/dist/core-utils-stack.js +47 -0
- package/dist/core-utils-types-hkt.d.ts +12 -0
- package/dist/core-utils-types-hkt.d.ts.map +1 -0
- package/dist/core-utils-types-hkt.js +1 -0
- package/dist/core-utils-types.d.ts +83 -0
- package/dist/core-utils-types.d.ts.map +1 -0
- package/dist/core-utils-types.js +39 -0
- package/dist/core-utils-validations.d.ts +21 -0
- package/dist/core-utils-validations.d.ts.map +1 -0
- package/dist/core-utils-validations.js +18 -0
- package/dist/core-utils.d.ts +39 -0
- package/dist/core-utils.d.ts.map +1 -0
- package/dist/core-utils.js +113 -0
- package/dist/es2015/core-utils-chrono.js +16 -0
- package/dist/es2015/core-utils-comparator.js +22 -0
- package/dist/es2015/core-utils-file.js +1 -0
- package/dist/es2015/core-utils-filesystem.js +157 -0
- package/dist/es2015/core-utils-fqn.js +19 -0
- package/dist/es2015/core-utils-pathway-resolver.js +31 -0
- package/dist/es2015/core-utils-pathway.js +206 -0
- package/dist/es2015/core-utils-registry.js +22 -0
- package/dist/es2015/core-utils-resolver.js +68 -0
- package/dist/es2015/core-utils-stack.js +47 -0
- package/dist/es2015/core-utils-types-hkt.js +1 -0
- package/dist/es2015/core-utils-types.js +39 -0
- package/dist/es2015/core-utils-validations.js +18 -0
- package/dist/es2015/core-utils.js +113 -0
- package/dist/es2015/index.js +24 -0
- package/dist/es2015/itinerary/hyper-layer-node.js +21 -0
- package/dist/es2015/itinerary/itinerary.js +276 -0
- package/dist/es2015/itinerary/stack.js +47 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/itinerary/hyper-layer-node.d.ts +12 -0
- package/dist/itinerary/hyper-layer-node.d.ts.map +1 -0
- package/dist/itinerary/hyper-layer-node.js +21 -0
- package/dist/itinerary/itinerary.d.ts +86 -0
- package/dist/itinerary/itinerary.d.ts.map +1 -0
- package/dist/itinerary/itinerary.js +275 -0
- package/dist/itinerary/stack.d.ts +20 -0
- package/dist/itinerary/stack.d.ts.map +1 -0
- package/dist/itinerary/stack.js +47 -0
- package/package.json +41 -0
- package/src/core-utils-chrono.ts +21 -0
- package/src/core-utils-comparator.ts +27 -0
- package/src/core-utils-file.ts +10 -0
- package/src/core-utils-filesystem.spec.ts +63 -0
- package/src/core-utils-filesystem.ts +232 -0
- package/src/core-utils-fqn.ts +17 -0
- package/src/core-utils-pathway-resolver.ts +36 -0
- package/src/core-utils-pathway.ts +232 -0
- package/src/core-utils-registry.ts +23 -0
- package/src/core-utils-resolver.ts +75 -0
- package/src/core-utils-stack.spec.ts +42 -0
- package/src/core-utils-stack.ts +62 -0
- package/src/core-utils-types-hkt.ts +9 -0
- package/src/core-utils-types.ts +127 -0
- package/src/core-utils-validations.ts +36 -0
- package/src/core-utils.spec.ts +56 -0
- package/src/core-utils.ts +139 -0
- package/src/index.ts +34 -0
- package/src/itinerary/hyper-layer-node.ts +25 -0
- package/src/itinerary/itinerary.spec.ts +388 -0
- package/src/itinerary/itinerary.ts +363 -0
- package/src/itinerary/stack.spec.ts +46 -0
- package/src/itinerary/stack.ts +62 -0
- package/tsconfig.es2015.json +24 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { StackElement } from "./stack";
|
|
2
|
+
import { HyperLayerNode } from "./hyper-layer-node";
|
|
3
|
+
export type ObjectId = string | number;
|
|
4
|
+
interface PropertyValuePair<P = string, V = any> {
|
|
5
|
+
propertyName: P;
|
|
6
|
+
propertyValue: V;
|
|
7
|
+
}
|
|
8
|
+
export interface ItineraryStepBase {
|
|
9
|
+
type: "property" | "lookup" | "position";
|
|
10
|
+
}
|
|
11
|
+
export interface PropertyStep extends ItineraryStepBase {
|
|
12
|
+
type: "property";
|
|
13
|
+
propertyName: string;
|
|
14
|
+
}
|
|
15
|
+
export interface LookupStep extends ItineraryStepBase {
|
|
16
|
+
type: "lookup";
|
|
17
|
+
keys: PropertyValuePair[];
|
|
18
|
+
}
|
|
19
|
+
export interface PositionStep extends ItineraryStepBase {
|
|
20
|
+
type: "position";
|
|
21
|
+
position: number;
|
|
22
|
+
}
|
|
23
|
+
export type ItineraryStep = PropertyStep | LookupStep | PositionStep;
|
|
24
|
+
export type Itinerary = StackElement<ItineraryStep>;
|
|
25
|
+
export interface ConstructiveItinerary {
|
|
26
|
+
itinerary: Itinerary | undefined;
|
|
27
|
+
prototypes: StackElement<any> | undefined;
|
|
28
|
+
}
|
|
29
|
+
export type StepValueNodeType = Array<any> | Record<string | number, any>;
|
|
30
|
+
export declare function constructiveItinerary(steps: Array<string | number | StepValueNodeType>): ConstructiveItinerary;
|
|
31
|
+
export declare function areSameItineraryStep(step1: ItineraryStep | null | undefined, step2: ItineraryStep | null | undefined): boolean;
|
|
32
|
+
export declare function itineraryOf(...args: (string | number)[]): Itinerary | undefined;
|
|
33
|
+
export declare function itineraryOverlay(it: Itinerary | undefined): Itinerary;
|
|
34
|
+
export declare function itineraryToStepArray(itinerary: Itinerary | undefined): ItineraryStep[];
|
|
35
|
+
export declare function propertyStep(propertyName: string): PropertyStep;
|
|
36
|
+
export declare function positionStep(position: number): PositionStep;
|
|
37
|
+
export declare function lookupStep(criteria: Record<string, any>): LookupStep;
|
|
38
|
+
export declare function xpathFromStack(path?: StackElement<ItineraryStep>): string;
|
|
39
|
+
export declare function navigate(from: any, itinerary: Itinerary | undefined, prototype?: StackElement<any>): any;
|
|
40
|
+
export declare function navigateStep(from: any, step: ItineraryStep, prototype?: any): any;
|
|
41
|
+
export declare function navigationDepth(from: any, itinerary: Itinerary | undefined, originalPath?: string): {
|
|
42
|
+
value: any;
|
|
43
|
+
path: string;
|
|
44
|
+
originalPath: string;
|
|
45
|
+
};
|
|
46
|
+
declare class OverlayNavigator {
|
|
47
|
+
navigate<T>(from: HyperLayerNode<T>, itinerary: Itinerary | undefined): HyperLayerNode<T> | undefined;
|
|
48
|
+
protected navigateStep<T>(from: HyperLayerNode<T>, step: ItineraryStep): HyperLayerNode<T> | undefined;
|
|
49
|
+
}
|
|
50
|
+
export declare const overlayNavigator: OverlayNavigator;
|
|
51
|
+
/**
|
|
52
|
+
* Same as {navigate} but try to path missing data node using prototype parameter
|
|
53
|
+
* @param from
|
|
54
|
+
* @param itinerary
|
|
55
|
+
* @param prototype
|
|
56
|
+
*/
|
|
57
|
+
export declare function ensurePath(from: any, itinerary: Itinerary | undefined, prototype?: any): any;
|
|
58
|
+
interface PointerBase {
|
|
59
|
+
type: "absolute" | "relative";
|
|
60
|
+
itinerary: ItineraryStep[];
|
|
61
|
+
}
|
|
62
|
+
export interface AbsolutePointer extends PointerBase {
|
|
63
|
+
type: "absolute";
|
|
64
|
+
}
|
|
65
|
+
export interface RelativePointer extends PointerBase {
|
|
66
|
+
type: "relative";
|
|
67
|
+
originUuid: ObjectId;
|
|
68
|
+
}
|
|
69
|
+
declare abstract class AbstractPointerBuilder {
|
|
70
|
+
readonly itinerary: ItineraryStep[];
|
|
71
|
+
child(propertyName: string): this;
|
|
72
|
+
lookup(...criterion: [string, any]): void;
|
|
73
|
+
}
|
|
74
|
+
declare class RelativePointerBuilder extends AbstractPointerBuilder implements RelativePointer {
|
|
75
|
+
originUuid: ObjectId;
|
|
76
|
+
readonly type = "relative";
|
|
77
|
+
constructor(originUuid: ObjectId);
|
|
78
|
+
}
|
|
79
|
+
declare class AbsolutePointerBuilder extends AbstractPointerBuilder implements AbsolutePointer {
|
|
80
|
+
readonly type = "absolute";
|
|
81
|
+
constructor();
|
|
82
|
+
}
|
|
83
|
+
export declare function pointer(fromId?: ObjectId): AbsolutePointerBuilder | RelativePointerBuilder;
|
|
84
|
+
export declare const ROOT: AbsolutePointer;
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=itinerary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"itinerary.d.ts","sourceRoot":"","sources":["../../src/itinerary/itinerary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import stack from "./stack";
|
|
2
|
+
export function constructiveItinerary(steps) {
|
|
3
|
+
const segments = [];
|
|
4
|
+
const prototypes = [];
|
|
5
|
+
for (let i = 0; i < steps.length; i += 2) {
|
|
6
|
+
const segment = steps[i];
|
|
7
|
+
const prototype = steps[i + 1];
|
|
8
|
+
switch (typeof segment) {
|
|
9
|
+
case "string":
|
|
10
|
+
case "number":
|
|
11
|
+
{
|
|
12
|
+
const step = typeof segment === "number" ? positionStep(segment) : propertyStep(segment);
|
|
13
|
+
switch (typeof prototype) {
|
|
14
|
+
case "object":
|
|
15
|
+
{ // including Array
|
|
16
|
+
segments.push(segment);
|
|
17
|
+
prototypes.push(prototype);
|
|
18
|
+
}
|
|
19
|
+
break;
|
|
20
|
+
default: {
|
|
21
|
+
throw new Error(`Expecting array or object but got : '${typeof prototype}'`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
break;
|
|
26
|
+
default: {
|
|
27
|
+
throw new Error(`Expecting string or number but gor : '${typeof segment}'`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
itinerary: itineraryOf(...segments),
|
|
33
|
+
prototypes: stack.fromArray(prototypes)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function areSameItineraryStep(step1, step2) {
|
|
37
|
+
if (step1 == undefined || step2 == undefined) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
else if (step1.type !== step2.type) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
switch (step1.type) {
|
|
45
|
+
case "property": return step2.type === "property" && step1.propertyName === step2.propertyName;
|
|
46
|
+
case "position": return step2.type === "position" && step1.position === step2.position;
|
|
47
|
+
case "lookup": {
|
|
48
|
+
return step2.type === "lookup"
|
|
49
|
+
&& step1.keys?.length === step2.keys?.length
|
|
50
|
+
&& step1.keys?.every(pv1 => step2.keys.some(pv2 => areSamePropertyValuePair(pv1, pv2)));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function areSamePropertyValuePair(p1, p2) {
|
|
56
|
+
return p1.propertyName === p2.propertyName
|
|
57
|
+
&& p1.propertyValue === p2.propertyValue;
|
|
58
|
+
}
|
|
59
|
+
export function itineraryOf(...args) {
|
|
60
|
+
return args.reduce((parent, e) => {
|
|
61
|
+
const value = typeof e === 'string' ? propertyStep(e) : positionStep(e);
|
|
62
|
+
return { parent, value };
|
|
63
|
+
}, undefined);
|
|
64
|
+
}
|
|
65
|
+
const dataStep = propertyStep("data");
|
|
66
|
+
const childrenStep = propertyStep("children");
|
|
67
|
+
export function itineraryOverlay(it) {
|
|
68
|
+
return { value: dataStep, parent: itineraryOverlayInner(it) };
|
|
69
|
+
}
|
|
70
|
+
function itineraryOverlayInner(it) {
|
|
71
|
+
if (it === undefined) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const { value, parent } = it;
|
|
76
|
+
return { value, parent: { value: childrenStep, parent: itineraryOverlayInner(parent) } };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function itineraryToStepArray(itinerary) {
|
|
80
|
+
let itn = itinerary;
|
|
81
|
+
const result = [];
|
|
82
|
+
while (itn !== undefined) {
|
|
83
|
+
result.push(itn.value);
|
|
84
|
+
itn = itn.parent;
|
|
85
|
+
}
|
|
86
|
+
return result.reverse();
|
|
87
|
+
}
|
|
88
|
+
export function propertyStep(propertyName) {
|
|
89
|
+
return { type: "property", propertyName };
|
|
90
|
+
}
|
|
91
|
+
export function positionStep(position) {
|
|
92
|
+
return { type: "position", position };
|
|
93
|
+
}
|
|
94
|
+
export function lookupStep(criteria) {
|
|
95
|
+
return { type: "lookup", keys: Object.entries(criteria).map(([k, v]) => ({ propertyName: k, propertyValue: v })) };
|
|
96
|
+
}
|
|
97
|
+
export function xpathFromStack(path) {
|
|
98
|
+
if (path === undefined) {
|
|
99
|
+
return ''; // TODO may need to return '.' here, to be checked
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const { value, parent } = path;
|
|
103
|
+
const parentXPath = xpathFromStack(parent);
|
|
104
|
+
switch (value.type) {
|
|
105
|
+
case 'property': {
|
|
106
|
+
return `${parentXPath}/${value.propertyName}`;
|
|
107
|
+
}
|
|
108
|
+
case 'position': {
|
|
109
|
+
return `${parentXPath}/*[${value.position + 1}]`;
|
|
110
|
+
}
|
|
111
|
+
case 'lookup': {
|
|
112
|
+
const predicate = value.keys.map(({ propertyName, propertyValue }) => `${propertyName}=$${propertyName}`).join(' and ');
|
|
113
|
+
return `${parentXPath}/*[${predicate}]`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export function navigate(from, itinerary, prototype) {
|
|
119
|
+
if (from === undefined) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
if (itinerary === undefined) {
|
|
123
|
+
return from;
|
|
124
|
+
}
|
|
125
|
+
const { parent, value: step } = itinerary;
|
|
126
|
+
const current = navigate(from, parent, prototype?.parent);
|
|
127
|
+
return navigateStep(current, step, prototype?.value);
|
|
128
|
+
}
|
|
129
|
+
export function navigateStep(from, step, prototype) {
|
|
130
|
+
if (from === undefined) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
if (step === undefined) {
|
|
134
|
+
return from;
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(from)) {
|
|
137
|
+
if (step.type === 'lookup') {
|
|
138
|
+
const stp = step;
|
|
139
|
+
const result = from.find(e => e !== undefined && stp.keys.every(({ propertyName, propertyValue }) => e[propertyName] === propertyValue));
|
|
140
|
+
if (result === undefined && prototype !== undefined) {
|
|
141
|
+
from.push(prototype);
|
|
142
|
+
return prototype;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (step.type === 'position') {
|
|
149
|
+
const result = from[step.position];
|
|
150
|
+
if (result === undefined && prototype !== undefined) {
|
|
151
|
+
from[step.position] = prototype;
|
|
152
|
+
return prototype;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (typeof from === 'object') {
|
|
160
|
+
if (step.type === 'property') {
|
|
161
|
+
const result = from === null ? undefined : from[step.propertyName];
|
|
162
|
+
if (result === undefined && prototype !== undefined && from !== null && from !== undefined) {
|
|
163
|
+
from[step.propertyName] = prototype;
|
|
164
|
+
return prototype;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
export function navigationDepth(from, itinerary, originalPath = xpathFromStack(itinerary)) {
|
|
174
|
+
if (from === undefined || from === null) {
|
|
175
|
+
return { value: undefined, path: '', originalPath };
|
|
176
|
+
}
|
|
177
|
+
if (itinerary === undefined) {
|
|
178
|
+
return { value: from, path: '', originalPath };
|
|
179
|
+
}
|
|
180
|
+
const { parent, value: step } = itinerary;
|
|
181
|
+
const current = navigationDepth(from, parent, originalPath);
|
|
182
|
+
if (current.value === undefined || current.value === null) {
|
|
183
|
+
return current;
|
|
184
|
+
}
|
|
185
|
+
const next = navigateStep(current.value, step);
|
|
186
|
+
if (next === undefined || next === null) {
|
|
187
|
+
return { value: current.value, path: xpathFromStack(parent), originalPath };
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return { value: next, path: xpathFromStack(itinerary), originalPath };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
class OverlayNavigator {
|
|
194
|
+
navigate(from, itinerary) {
|
|
195
|
+
if (from === undefined) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
if (itinerary === undefined) {
|
|
199
|
+
return from;
|
|
200
|
+
}
|
|
201
|
+
const { parent, value: step } = itinerary;
|
|
202
|
+
const current = this.navigate(from, parent);
|
|
203
|
+
return current === undefined ? undefined : this.navigateStep(current, step);
|
|
204
|
+
}
|
|
205
|
+
navigateStep(from, step) {
|
|
206
|
+
if (from === undefined) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
if (step === undefined) {
|
|
210
|
+
return from;
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(from.children)) {
|
|
213
|
+
if (step.type === 'lookup') {
|
|
214
|
+
const stp = step;
|
|
215
|
+
const result = from.children.find(e => e !== undefined && stp.keys.every(({ propertyName, propertyValue }) => e.keys?.[propertyName] === propertyValue));
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
else if (step.type === 'position') {
|
|
219
|
+
const result = from.children[step.position];
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (from.children !== null && typeof from.children === 'object') {
|
|
224
|
+
if (step.type === 'property') {
|
|
225
|
+
const result = from === null ? undefined : from.children[step.propertyName];
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
export const overlayNavigator = new OverlayNavigator();
|
|
233
|
+
/**
|
|
234
|
+
* Same as {navigate} but try to path missing data node using prototype parameter
|
|
235
|
+
* @param from
|
|
236
|
+
* @param itinerary
|
|
237
|
+
* @param prototype
|
|
238
|
+
*/
|
|
239
|
+
export function ensurePath(from, itinerary, prototype) {
|
|
240
|
+
return navigate(from, itinerary, prototype);
|
|
241
|
+
}
|
|
242
|
+
class AbstractPointerBuilder {
|
|
243
|
+
constructor() {
|
|
244
|
+
this.itinerary = [];
|
|
245
|
+
}
|
|
246
|
+
child(propertyName) {
|
|
247
|
+
this.itinerary.push({ type: "property", propertyName });
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
lookup(...criterion) {
|
|
251
|
+
criterion.forEach(([propertyName, propertyValue]) => {
|
|
252
|
+
this.itinerary.push({ type: "lookup", keys: [{ propertyName, propertyValue }] });
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
class RelativePointerBuilder extends AbstractPointerBuilder {
|
|
257
|
+
constructor(originUuid) {
|
|
258
|
+
super();
|
|
259
|
+
this.originUuid = originUuid;
|
|
260
|
+
this.type = "relative";
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
class AbsolutePointerBuilder extends AbstractPointerBuilder {
|
|
264
|
+
constructor() {
|
|
265
|
+
super();
|
|
266
|
+
this.type = "absolute";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
export function pointer(fromId) {
|
|
270
|
+
if (fromId === undefined) {
|
|
271
|
+
return new AbsolutePointerBuilder();
|
|
272
|
+
}
|
|
273
|
+
return new RelativePointerBuilder(fromId);
|
|
274
|
+
}
|
|
275
|
+
export const ROOT = Object.freeze({ type: "absolute", itinerary: [] });
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface StackElement<T> {
|
|
2
|
+
value: T;
|
|
3
|
+
parent?: StackElement<T>;
|
|
4
|
+
}
|
|
5
|
+
export declare function head<T>(value: T): StackElement<T>;
|
|
6
|
+
export declare function push<T>(head: StackElement<T> | undefined, value: T): StackElement<T>;
|
|
7
|
+
export declare function concat<T>(tail: StackElement<T> | undefined, stack: StackElement<T> | undefined): StackElement<T> | undefined;
|
|
8
|
+
export declare function fromArray<T = any>(t: T[] | undefined): StackElement<T> | undefined;
|
|
9
|
+
export declare function reduce<T, R>(head: StackElement<T> | undefined, reducer: (r: R, t: T) => R, initial: R): R;
|
|
10
|
+
declare function equal<T>(a: StackElement<T> | null | undefined, b: StackElement<T> | null | undefined): boolean;
|
|
11
|
+
declare const stack: {
|
|
12
|
+
push: typeof push;
|
|
13
|
+
head: typeof head;
|
|
14
|
+
concat: typeof concat;
|
|
15
|
+
reduce: typeof reduce;
|
|
16
|
+
fromArray: typeof fromArray;
|
|
17
|
+
equal: typeof equal;
|
|
18
|
+
};
|
|
19
|
+
export default stack;
|
|
20
|
+
//# sourceMappingURL=stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../src/itinerary/stack.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,KAAK,EAAG,CAAC,CAAA;IACT,MAAM,CAAC,EAAG,YAAY,CAAC,CAAC,CAAC,CAAA;CAC1B;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAG,CAAC,GAAI,YAAY,CAAC,CAAC,CAAC,CAEnD;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,IAAI,EAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,EAAG,CAAC,GAAI,YAAY,CAAC,CAAC,CAAC,CAEvF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,EAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,GAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAO/H;AAED,wBAAgB,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,EAAG,CAAC,EAAE,GAAG,SAAS,GAAI,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAEpF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAG,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,OAAO,EAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAG,CAAC,GAAI,CAAC,CAQ9G;AAED,iBAAS,KAAK,CAAC,CAAC,EAAE,CAAC,EAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAAE,CAAC,EAAG,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAAI,OAAO,CAqBxG;AAED,QAAA,MAAM,KAAK;;;;;;;CAAiD,CAAA;AAE5D,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function head(value) {
|
|
2
|
+
return { value };
|
|
3
|
+
}
|
|
4
|
+
export function push(head, value) {
|
|
5
|
+
return { value, parent: head };
|
|
6
|
+
}
|
|
7
|
+
export function concat(tail, stack) {
|
|
8
|
+
if (tail === undefined)
|
|
9
|
+
return stack;
|
|
10
|
+
if (stack === undefined)
|
|
11
|
+
return tail;
|
|
12
|
+
const { parent, value } = stack;
|
|
13
|
+
return { value, parent: concat(tail, parent) };
|
|
14
|
+
}
|
|
15
|
+
export function fromArray(t) {
|
|
16
|
+
return t?.reduce((acc, t) => push(acc, t), undefined);
|
|
17
|
+
}
|
|
18
|
+
export function reduce(head, reducer, initial) {
|
|
19
|
+
if (head === undefined) {
|
|
20
|
+
return initial;
|
|
21
|
+
}
|
|
22
|
+
const { parent, value } = head;
|
|
23
|
+
const acc = reduce(parent, reducer, initial);
|
|
24
|
+
return reducer(acc, value);
|
|
25
|
+
}
|
|
26
|
+
function equal(a, b) {
|
|
27
|
+
// Not equal if both are nullish
|
|
28
|
+
if (a === undefined || a === null || b === undefined || b === null) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
while (a !== undefined && b !== undefined) {
|
|
32
|
+
if (a.value !== b.value) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
a = a.parent;
|
|
36
|
+
b = b.parent;
|
|
37
|
+
}
|
|
38
|
+
// Equal if traversed all stack at both side
|
|
39
|
+
if ((a === undefined || a === null) && (b === undefined || b === null)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const stack = { push, head, concat, reduce, fromArray, equal };
|
|
47
|
+
export default stack;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hamak/shared-utils",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Shared utilities for path navigation, filesystem operations, and common helpers",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/amah/app-framework.git",
|
|
13
|
+
"directory": "packages/utils/shared-utils"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json && tsc -p tsconfig.es2015.json",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest"
|
|
23
|
+
},
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"default": "./dist/index.js",
|
|
29
|
+
"legacy": "./dist/es2015/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./es2015": {
|
|
32
|
+
"import": "./dist/es2015/index.js",
|
|
33
|
+
"default": "./dist/es2015/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"typescript": "~5.4.0",
|
|
39
|
+
"vitest": "^2.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class Chronometre {
|
|
2
|
+
private start : number
|
|
3
|
+
constructor() {
|
|
4
|
+
this.start = Date.now()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
duration() : number {
|
|
8
|
+
return Date.now() - this.start
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
restart() : number {
|
|
12
|
+
const d = this.duration()
|
|
13
|
+
this.start = Date.now()
|
|
14
|
+
|
|
15
|
+
return d
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function chronometre() {
|
|
20
|
+
return new Chronometre()
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type Order = -1 | 0 | 1
|
|
2
|
+
export type Weighted<T, W> = {value: T, weight: W}
|
|
3
|
+
export type WeightFn<T> = (t : T) => number
|
|
4
|
+
export type CompareFn<T> = (a : T, b : T, ...fns : [WeightFn<T>, ...WeightFn<T>[]]) => Order
|
|
5
|
+
export function reverseOrder<T>(compareFn : CompareFn<T>) : CompareFn<T> {
|
|
6
|
+
return (a, b, fns) => - compareFn(a, b, fns) as Order
|
|
7
|
+
}
|
|
8
|
+
export function compare<T>(a : T, b : T, ...fns : WeightFn<T>[]) : Order {
|
|
9
|
+
if(fns.length === 0){
|
|
10
|
+
if(typeof a === "number" && typeof b === "number"){
|
|
11
|
+
fns = [(x : T) => x as unknown as number]
|
|
12
|
+
} else {
|
|
13
|
+
throw new Error(`Expecting at least one weight function when arguments are not numbers`)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
for (const weightFn of fns) {
|
|
18
|
+
const aWeight = weightFn(a)
|
|
19
|
+
const bWeight = weightFn(b)
|
|
20
|
+
if(aWeight != bWeight){
|
|
21
|
+
return a < b ? -1 : 1
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const compareDesc = reverseOrder(compare)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { DirectoryNode, fsutils } from './core-utils-filesystem';
|
|
2
|
+
describe("Core utils filesystem", () => {
|
|
3
|
+
it("fsutils createDirectory - creates a file with intermediate directories", () => {
|
|
4
|
+
const root = fsutils.createRootDir([]);
|
|
5
|
+
const file = fsutils.createDescendent(root, "a/b/c.txt", "hello");
|
|
6
|
+
|
|
7
|
+
expect(file).toBeDefined();
|
|
8
|
+
expect(file?.type).toBe("file");
|
|
9
|
+
expect(file?.content).toBe("hello");
|
|
10
|
+
|
|
11
|
+
const dirA = root.children["a"];
|
|
12
|
+
expect(dirA?.type).toBe("directory");
|
|
13
|
+
|
|
14
|
+
const dirB = (dirA as DirectoryNode).children["b"];
|
|
15
|
+
expect(dirB?.type).toBe("directory");
|
|
16
|
+
|
|
17
|
+
const fileC = (dirB as DirectoryNode).children["c.txt"];
|
|
18
|
+
expect(fileC?.type).toBe("file");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("fsutils createDirectory - throws if path conflicts with a file in intermediate segment", () => {
|
|
22
|
+
const conflict = fsutils.createFile("conflict", "not a dir");
|
|
23
|
+
const root = fsutils.createRootDir([conflict]);
|
|
24
|
+
|
|
25
|
+
expect(() =>
|
|
26
|
+
fsutils.createDescendent(root, "conflict/x.txt", "data")
|
|
27
|
+
).toThrow(/is a file/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("fsutils createDirectory - throws if file exists and override is false", () => {
|
|
31
|
+
const existing = fsutils.createFile("existing.txt", "original");
|
|
32
|
+
const root = fsutils.createRootDir([
|
|
33
|
+
fsutils.createDir("dir", [existing])
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
expect(() =>
|
|
37
|
+
fsutils.createDescendent(root, "dir/existing.txt", "new content", "xs:any", undefined, false)
|
|
38
|
+
).toThrow(/already exists/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("fsutils createDirectory - overrides file if override is true", () => {
|
|
42
|
+
const existing = fsutils.createFile("existing.txt", "original");
|
|
43
|
+
const root = fsutils.createRootDir([
|
|
44
|
+
fsutils.createDir("dir", [existing])
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const newFile = fsutils.createDescendent(root, "dir/existing.txt", "new content", "xs:any", undefined, true);
|
|
48
|
+
|
|
49
|
+
expect(newFile?.content).toBe("new content");
|
|
50
|
+
expect((root.children["dir"] as DirectoryNode).children["existing.txt"]).toBe(newFile);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("fsutils createDirectory - returns undefined if path is '.'", () => {
|
|
54
|
+
const root = fsutils.createRootDir([]);
|
|
55
|
+
const result = fsutils.createDescendent(root, ".", "data");
|
|
56
|
+
expect(result).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("fsutils createDirectory - returns undefined if fromDir is undefined", () => {
|
|
60
|
+
const result = fsutils.createDescendent(undefined, "a/b.txt", "data");
|
|
61
|
+
expect(result).toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
});
|