corebasic 1.0.230 → 1.0.231

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.
@@ -8,11 +8,16 @@ export async function suffix(doc, policies, insertMode, arg) {
8
8
  const value = typeof policy.value === "string" ? policy.value.trim() : undefined;
9
9
  if ("value" in policy && !value)
10
10
  throw new Error(`Error: Invalid suffix policy fixed 'value' specified for key in Dip`);
11
- let keys = value ? [value] : entries(policy.key, doc);
12
- const [policy_type, format] = (policy.type ?? "string").split(':');
11
+ let candidates = value ? [value] : entries(policy.key ?? '', doc);
12
+ const [policy_type, format_type] = (policy.type ?? "string").split(':');
13
+ const format = format_type ?? "";
13
14
  if (!EXPLICIT_SUFFIX_POLICY_TYPES.has(policy_type))
14
15
  throw new Error(`Error: Invalid suffix policy type specified for key ${policy.key} in Dip`);
15
- if (policy_type === "string" && keys.filter(item => typeof item === "object").length)
16
+ // ====== Range ======
17
+ let ranges = candidates.filter(item => typeof item === "object");
18
+ let keys = candidates.filter(item => typeof item !== "object");
19
+ // ====== Range ======
20
+ if (policy_type === "string" && ranges.length)
16
21
  throw new Error(`Error: Invalid numeric bounds on string type in suffix policy specified for key ${policy.key} in Dip`);
17
22
  if (policy_type === "date") {
18
23
  if (!format?.trim())
@@ -22,14 +27,12 @@ export async function suffix(doc, policies, insertMode, arg) {
22
27
  }
23
28
  const ls_threshold = policy.ls_threshold ?? 5000;
24
29
  // ====== Range ======
25
- let ranges = keys.filter(item => typeof item === "object");
26
- keys = keys.filter(item => typeof item !== "object");
27
30
  if (policy_type === "date") {
28
31
  keys = keys.map(key => formatDate(key, format));
29
32
  if (ranges.length) {
30
33
  const bounds = extractBounds(reduceRanges(ranges));
31
- const from = bounds.from ?? policy.min; // policy.min is always inclusive i.e $gte
32
- const to = bounds.to ?? policy.max; // policy.max is always inclusive i.e $lte
34
+ const from = (bounds.from ?? policy.min); // policy.min is always inclusive i.e $gte
35
+ const to = (bounds.to ?? policy.max); // policy.max is always inclusive i.e $lte
33
36
  const fromOp = bounds.from ? bounds.fromOp : "$gte"; // policy.min is $gte
34
37
  const toOp = bounds.to ? bounds.toOp : "$lte"; // policy.max is $lte
35
38
  if (from === undefined || to === undefined) {
@@ -38,8 +41,8 @@ export async function suffix(doc, policies, insertMode, arg) {
38
41
  }
39
42
  else {
40
43
  let ranges = fillDates({ from, to, fromOp, toOp }, format);
41
- if (!ranges.length && policy.inferRange)
42
- ranges = [...new Set([from === undefined ? undefined : formatDate(from, format), to === undefined ? undefined : formatDate(to, format)].filter(item => item))];
44
+ // if (!ranges.length && policy.inferRange)
45
+ // ranges = [...new Set([from === undefined ? undefined : formatDate(from, format), to === undefined ? undefined : formatDate(to, format)].filter(item => item))]
43
46
  // console.log(ranges)
44
47
  if (ranges.length > ls_threshold) {
45
48
  keys = []; // Too many suffixes to enumerate. Fall back to Dip.operation("ls").
@@ -53,8 +56,8 @@ export async function suffix(doc, policies, insertMode, arg) {
53
56
  }
54
57
  else if (policy_type === "number" && ranges.length) {
55
58
  const bounds = getNormalizedBounds(extractBounds(reduceRanges(ranges)), "number");
56
- const from = bounds.from ?? policy.min; // policy.min is always inclusive i.e $gte
57
- const to = bounds.to ?? policy.max; // policy.max is always inclusive i.e $lte
59
+ const from = (bounds.from ?? policy.min); // policy.min is always inclusive i.e $gte
60
+ const to = (bounds.to ?? policy.max); // policy.max is always inclusive i.e $lte
58
61
  if (from === undefined || to === undefined) {
59
62
  keys = []; // Fall back to Dip.operation("ls").
60
63
  console.warn(`Warn: Missing suffix policy min/max bound for ${policy_type} range specified for key ${policy.key} in Dip`);
@@ -79,8 +82,9 @@ export async function suffix(doc, policies, insertMode, arg) {
79
82
  if (!keys.length && !insertMode) {
80
83
  ls = true;
81
84
  for (const suffix of suffixes) {
82
- for (const collection of arg.collection) {
83
- const dirs = (await Dip.operation("ls", { db: arg.db, collection: collection, suffix })).filter(dir => !dir.startsWith('chunk-'));
85
+ const collections = arg?.collection ?? [];
86
+ for (const collection of collections) {
87
+ const dirs = (await Dip.operation("ls", { db: arg?.db, collection: collection, suffix })).filter((dir) => !dir.startsWith('chunk-'));
84
88
  suffixAssociatedKeys[suffix] = suffixAssociatedKeys[suffix] ?? [];
85
89
  suffixAssociatedKeys[suffix].push(...dirs);
86
90
  }
@@ -142,46 +142,33 @@ describe("fillDates()", () => {
142
142
  const result = fillDates({ from: "2026-01-01", to: "2026-12-31", fromOp: "$gte", toOp: "$lte" }, "YYYY-MM-DD");
143
143
  assert.equal(new Set(result).size, result.length);
144
144
  });
145
- test("excludes lower bound with $gt", () => {
146
- assert.deepEqual(fillDates({
147
- from: "2026-07-01",
148
- to: "2026-07-03",
149
- fromOp: "$gt",
150
- toOp: null,
151
- }, "YYYY-MM-DD"), [
152
- "2026-07-02",
153
- "2026-07-03",
154
- ]);
155
- });
156
- test("excludes upper bound with $lt", () => {
157
- assert.deepEqual(fillDates({
158
- from: "2026-07-01",
159
- to: "2026-07-03",
160
- fromOp: null,
161
- toOp: "$lt",
162
- }, "YYYY-MM-DD"), [
163
- "2026-07-01",
164
- "2026-07-02",
165
- ]);
166
- });
145
+ // test("excludes lower bound with $gt", () => {
146
+ // assert.deepEqual(
147
+ // fillDates({ from: "2026-07-01", to: "2026-07-03", fromOp: "$gt", toOp: null }, "YYYY-MM-DD"),
148
+ // [
149
+ // "2026-07-02",
150
+ // "2026-07-03",
151
+ // ]
152
+ // );
153
+ // });
154
+ //
155
+ // test("excludes upper bound with $lt", () => {
156
+ // assert.deepEqual(
157
+ // fillDates({ from: "2026-07-01", to: "2026-07-03", fromOp: null, toOp: "$lt" }, "YYYY-MM-DD"),
158
+ // [
159
+ // "2026-07-01",
160
+ // "2026-07-02",
161
+ // ]
162
+ // );
163
+ // });
167
164
  test("excludes both bounds", () => {
168
- assert.deepEqual(fillDates({
169
- from: "2026-07-01",
170
- to: "2026-07-05",
171
- fromOp: "$gt",
172
- toOp: "$lt",
173
- }, "YYYY-MM-DD"), [
165
+ assert.deepEqual(fillDates({ from: "2026-07-01", to: "2026-07-05", fromOp: "$gt", toOp: "$lt" }, "YYYY-MM-DD"), [
174
166
  "2026-07-02",
175
167
  "2026-07-03",
176
168
  "2026-07-04",
177
169
  ]);
178
170
  });
179
171
  test("returns empty when exclusive bounds collapse range", () => {
180
- assert.deepEqual(fillDates({
181
- from: "2026-07-01",
182
- to: "2026-07-02",
183
- fromOp: "$gt",
184
- toOp: "$lt",
185
- }, "YYYY-MM-DD"), []);
172
+ assert.deepEqual(fillDates({ from: "2026-07-01", to: "2026-07-02", fromOp: "$gt", toOp: "$lt" }, "YYYY-MM-DD"), []);
186
173
  });
187
174
  });
@@ -1,75 +1,56 @@
1
1
  import { describe, test } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { matchPolicy, overlaps, contains, intersects, applySuffixPolicy } from "../policy.js";
4
+ const defaultBounds = { values: [], from: -Infinity, to: Infinity, fromOp: "$gte", toOp: "$lte" };
4
5
  /* ============================================================
5
6
  * overlaps()
6
7
  * ============================================================ */
7
8
  describe("overlaps()", () => {
8
9
  test("overlaps: identical ranges", () => {
9
- assert.equal(overlaps({ from: 10, to: 20 }, { from: 10, to: 20 }), true);
10
+ assert.equal(overlaps({ ...defaultBounds, from: 10, to: 20 }, { ...defaultBounds, from: 10, to: 20 }), true);
10
11
  });
11
12
  test("overlaps: partial overlap", () => {
12
- assert.equal(overlaps({ from: 10, to: 20 }, { from: 15, to: 25 }), true);
13
+ assert.equal(overlaps({ ...defaultBounds, from: 10, to: 20 }, { ...defaultBounds, from: 15, to: 25 }), true);
13
14
  });
14
15
  test("overlaps: no overlap", () => {
15
- assert.equal(overlaps({ from: 10, to: 20 }, { from: 21, to: 30 }), false);
16
+ assert.equal(overlaps({ ...defaultBounds, from: 10, to: 20 }, { ...defaultBounds, from: 21, to: 30 }), false);
16
17
  });
17
18
  test("overlaps: open ended lower", () => {
18
- assert.equal(overlaps({ to: 10 }, { from: 5 }), true);
19
+ assert.equal(overlaps({ ...defaultBounds, to: 10 }, { ...defaultBounds, from: 5 }), true);
19
20
  });
20
21
  test("overlaps: open ended upper", () => {
21
- assert.equal(overlaps({ from: 20 }, { to: 10 }), false);
22
+ assert.equal(overlaps({ ...defaultBounds, from: 20 }, { ...defaultBounds, to: 10 }), false);
22
23
  });
23
24
  describe("overlaps() boundary semantics", () => {
24
25
  test("touching: open/open does not overlap", () => {
25
- assert.equal(overlaps({ to: 10, toOp: "$lt" }, { from: 10, fromOp: "$gt" }), false);
26
+ assert.equal(overlaps({ ...defaultBounds, to: 10, toOp: "$lt" }, { ...defaultBounds, from: 10, fromOp: "$gt" }), false);
26
27
  });
27
28
  test("touching: closed/closed overlaps", () => {
28
- assert.equal(overlaps({ to: 10, toOp: "$lte" }, { from: 10, fromOp: "$gte" }), true);
29
+ assert.equal(overlaps({ ...defaultBounds, to: 10, toOp: "$lte" }, { ...defaultBounds, from: 10, fromOp: "$gte" }), true);
29
30
  });
30
31
  test("touching: closed/open does not overlap", () => {
31
- assert.equal(overlaps({ to: 10, toOp: "$lte" }, { from: 10, fromOp: "$gt" }), false);
32
+ assert.equal(overlaps({ ...defaultBounds, to: 10, toOp: "$lte" }, { ...defaultBounds, from: 10, fromOp: "$gt" }), false);
32
33
  });
33
34
  test("touching: open/closed does not overlap", () => {
34
- assert.equal(overlaps({ to: 10, toOp: "$lt" }, { from: 10, fromOp: "$gte" }), false);
35
+ assert.equal(overlaps({ ...defaultBounds, to: 10, toOp: "$lt" }, { ...defaultBounds, from: 10, fromOp: "$gte" }), false);
35
36
  });
36
37
  test("touching: reverse order closed/closed overlaps", () => {
37
- assert.equal(overlaps({ from: 10, fromOp: "$gte" }, { to: 10, toOp: "$lte" }), true);
38
+ assert.equal(overlaps({ ...defaultBounds, from: 10, fromOp: "$gte" }, { ...defaultBounds, to: 10, toOp: "$lte" }), true);
38
39
  });
39
40
  test("touching: reverse order open/open does not overlap", () => {
40
- assert.equal(overlaps({ from: 10, fromOp: "$gt" }, { to: 10, toOp: "$lt" }), false);
41
+ assert.equal(overlaps({ ...defaultBounds, from: 10, fromOp: "$gt" }, { ...defaultBounds, to: 10, toOp: "$lt" }), false);
41
42
  });
42
43
  test("identical single-point closed intervals overlap", () => {
43
- assert.equal(overlaps({
44
- from: 10,
45
- to: 10,
46
- fromOp: "$gte",
47
- toOp: "$lte"
48
- }, {
49
- from: 10,
50
- to: 10,
51
- fromOp: "$gte",
52
- toOp: "$lte"
53
- }), true);
44
+ assert.equal(overlaps({ ...defaultBounds, from: 10, to: 10, fromOp: "$gte", toOp: "$lte" }, { ...defaultBounds, from: 10, to: 10, fromOp: "$gte", toOp: "$lte" }), true);
54
45
  });
55
46
  test("identical single-point open intervals do not overlap", () => {
56
- assert.equal(overlaps({
57
- from: 10,
58
- to: 10,
59
- fromOp: "$gt",
60
- toOp: "$lt"
61
- }, {
62
- from: 10,
63
- to: 10,
64
- fromOp: "$gt",
65
- toOp: "$lt"
66
- }), false);
47
+ assert.equal(overlaps({ ...defaultBounds, from: 10, to: 10, fromOp: "$gt", toOp: "$lt" }, { ...defaultBounds, from: 10, to: 10, fromOp: "$gt", toOp: "$lt" }), false);
67
48
  });
68
49
  test("infinite lower bound with touching closed endpoint overlaps", () => {
69
- assert.equal(overlaps({ to: 5, toOp: "$lte" }, { from: 5, fromOp: "$gte" }), true);
50
+ assert.equal(overlaps({ ...defaultBounds, to: 5, toOp: "$lte" }, { ...defaultBounds, from: 5, fromOp: "$gte" }), true);
70
51
  });
71
52
  test("infinite lower bound with touching open endpoint does not overlap", () => {
72
- assert.equal(overlaps({ to: 5, toOp: "$lt" }, { from: 5, fromOp: "$gt" }), false);
53
+ assert.equal(overlaps({ ...defaultBounds, to: 5, toOp: "$lt" }, { ...defaultBounds, from: 5, fromOp: "$gt" }), false);
73
54
  });
74
55
  });
75
56
  });
@@ -78,89 +59,69 @@ describe("overlaps()", () => {
78
59
  * ============================================================ */
79
60
  describe("contains()", () => {
80
61
  test("contains: inside", () => {
81
- assert.equal(contains({ from: 10, to: 20 }, 15), true);
62
+ assert.equal(contains({ ...defaultBounds, from: 10, to: 20 }, 15), true);
82
63
  });
83
64
  test("contains: below", () => {
84
- assert.equal(contains({ from: 10, to: 20 }, 5), false);
65
+ assert.equal(contains({ ...defaultBounds, from: 10, to: 20 }, 5), false);
85
66
  });
86
67
  test("contains: above", () => {
87
- assert.equal(contains({ from: 10, to: 20 }, 25), false);
68
+ assert.equal(contains({ ...defaultBounds, from: 10, to: 20 }, 25), false);
88
69
  });
89
70
  test("contains: lower bound", () => {
90
- assert.equal(contains({ from: 10, to: 20 }, 10), true);
71
+ assert.equal(contains({ ...defaultBounds, from: 10, to: 20 }, 10), true);
91
72
  });
92
73
  test("contains: upper bound", () => {
93
- assert.equal(contains({ from: 10, to: 20 }, 20), true);
74
+ assert.equal(contains({ ...defaultBounds, from: 10, to: 20 }, 20), true);
94
75
  });
95
76
  test("contains: gt excludes lower bound", () => {
96
- assert.equal(contains({ from: 5, fromOp: "$gt" }, 5), false);
77
+ assert.equal(contains({ ...defaultBounds, from: 5, fromOp: "$gt" }, 5), false);
97
78
  });
98
79
  test("contains: gte includes lower bound", () => {
99
- assert.equal(contains({ from: 5, fromOp: "$gte" }, 5), true);
80
+ assert.equal(contains({ ...defaultBounds, from: 5, fromOp: "$gte" }, 5), true);
100
81
  });
101
82
  test("contains: lt excludes upper bound", () => {
102
- assert.equal(contains({ to: 10, toOp: "$lt" }, 10), false);
83
+ assert.equal(contains({ ...defaultBounds, to: 10, toOp: "$lt" }, 10), false);
103
84
  });
104
85
  test("contains: lte includes upper bound", () => {
105
- assert.equal(contains({ to: 10, toOp: "$lte" }, 10), true);
86
+ assert.equal(contains({ ...defaultBounds, to: 10, toOp: "$lte" }, 10), true);
106
87
  });
107
88
  test("contains: gt/lt excludes both endpoints", () => {
108
- const bounds = {
109
- from: 5,
110
- fromOp: "$gt",
111
- to: 10,
112
- toOp: "$lt"
113
- };
89
+ const bounds = { ...defaultBounds, from: 5, fromOp: "$gt", to: 10, toOp: "$lt" };
114
90
  assert.equal(contains(bounds, 5), false);
115
91
  assert.equal(contains(bounds, 10), false);
116
92
  assert.equal(contains(bounds, 7), true);
117
93
  });
118
94
  test("contains: gte/lte includes both endpoints", () => {
119
- const bounds = {
120
- from: 5,
121
- fromOp: "$gte",
122
- to: 10,
123
- toOp: "$lte"
124
- };
95
+ const bounds = { ...defaultBounds, from: 5, fromOp: "$gte", to: 10, toOp: "$lte" };
125
96
  assert.equal(contains(bounds, 5), true);
126
97
  assert.equal(contains(bounds, 10), true);
127
98
  assert.equal(contains(bounds, 7), true);
128
99
  });
129
100
  test("contains: gt/lte mixed", () => {
130
- const bounds = {
131
- from: 5,
132
- fromOp: "$gt",
133
- to: 10,
134
- toOp: "$lte"
135
- };
101
+ const bounds = { ...defaultBounds, from: 5, fromOp: "$gt", to: 10, toOp: "$lte" };
136
102
  assert.equal(contains(bounds, 5), false);
137
103
  assert.equal(contains(bounds, 10), true);
138
104
  });
139
105
  test("contains: gte/lt mixed", () => {
140
- const bounds = {
141
- from: 5,
142
- fromOp: "$gte",
143
- to: 10,
144
- toOp: "$lt"
145
- };
106
+ const bounds = { ...defaultBounds, from: 5, fromOp: "$gte", to: 10, toOp: "$lt" };
146
107
  assert.equal(contains(bounds, 5), true);
147
108
  assert.equal(contains(bounds, 10), false);
148
109
  });
149
110
  test("contains: value below lower bound", () => {
150
- assert.equal(contains({ from: 5, fromOp: "$gte" }, 4), false);
111
+ assert.equal(contains({ ...defaultBounds, from: 5, fromOp: "$gte" }, 4), false);
151
112
  });
152
113
  test("contains: value above upper bound", () => {
153
- assert.equal(contains({ to: 10, toOp: "$lte" }, 11), false);
114
+ assert.equal(contains({ ...defaultBounds, to: 10, toOp: "$lte" }, 11), false);
154
115
  });
155
116
  test("contains: open lower bound", () => {
156
- assert.equal(contains({ to: 10, toOp: "$lte" }, -1000), true);
117
+ assert.equal(contains({ ...defaultBounds, to: 10, toOp: "$lte" }, -1000), true);
157
118
  });
158
119
  test("contains: open upper bound", () => {
159
- assert.equal(contains({ from: 5, fromOp: "$gte" }, 1000), true);
120
+ assert.equal(contains({ ...defaultBounds, from: 5, fromOp: "$gte" }, 1000), true);
160
121
  });
161
122
  test("contains: completely unbounded", () => {
162
- assert.equal(contains({}, -100), true);
163
- assert.equal(contains({}, 100), true);
123
+ assert.equal(contains({ ...defaultBounds, }, -100), true);
124
+ assert.equal(contains({ ...defaultBounds, }, 100), true);
164
125
  });
165
126
  });
166
127
  /* ============================================================
@@ -174,22 +135,22 @@ describe("intersects()", () => {
174
135
  assert.equal(intersects(["A"], null, ["B"], null), false);
175
136
  });
176
137
  test("intersects: value ↔ range", () => {
177
- assert.equal(intersects([10], null, [], { from: 5 }), true);
138
+ assert.equal(intersects([10], null, [], { ...defaultBounds, from: 5 }), true);
178
139
  });
179
140
  test("intersects: value ↔ range miss", () => {
180
- assert.equal(intersects([2], null, [], { from: 5 }), false);
141
+ assert.equal(intersects([2], null, [], { ...defaultBounds, from: 5 }), false);
181
142
  });
182
143
  test("intersects: range ↔ value", () => {
183
- assert.equal(intersects([], { from: 5 }, [10], null), true);
144
+ assert.equal(intersects([], { ...defaultBounds, from: 5 }, [10], null), true);
184
145
  });
185
146
  test("intersects: range ↔ value miss", () => {
186
- assert.equal(intersects([], { from: 5 }, [2], null), false);
147
+ assert.equal(intersects([], { ...defaultBounds, from: 5 }, [2], null), false);
187
148
  });
188
149
  test("intersects: range ↔ range", () => {
189
- assert.equal(intersects([], { from: 10 }, [], { from: 20 }), true);
150
+ assert.equal(intersects([], { ...defaultBounds, from: 10 }, [], { ...defaultBounds, from: 20 }), true);
190
151
  });
191
152
  test("intersects: range ↔ range miss", () => {
192
- assert.equal(intersects([], { to: 5 }, [], { from: 10 }), false);
153
+ assert.equal(intersects([], { ...defaultBounds, to: 5 }, [], { ...defaultBounds, from: 10 }), false);
193
154
  });
194
155
  });
195
156
  /* ============================================================
package/dist/libs/dip.js CHANGED
@@ -28,34 +28,36 @@ export async function insertSync(meta, feature, date, uid, ref, type) {
28
28
  await Elabase.insert(meta, collection, data);
29
29
  }
30
30
  export const IdempotentDip = () => {
31
- return {
31
+ const state = {
32
32
  txn: '',
33
33
  meta: undefined,
34
- new: (async function (meta, txn, blank) {
35
- this.txn = txn;
36
- this.meta = meta;
37
- return blank ? this : ((await Elabase.query(this.meta, "txns", { _id: this.txn })).length ? false : this);
38
- }),
39
- insert: (async function (collection, query, data) {
40
- await Elabase.update(this.meta, collection, query, { $setOnInsert: data }, { upsert: true });
41
- }),
42
- update: (async function (collection, query, data, rollback) {
43
- let token = { [this.txn]: true };
44
- let idempotent = { [this.txn]: { $exists: rollback ?? false } };
45
- let result = await Elabase.update(this.meta, collection, { ...query, ...idempotent }, { ...data, $setOnInsert: undefined, $set: { ...(data.$set ?? {}), ...token } });
46
- return result.count ? true : (await Elabase.query(this.meta, collection, { ...query, ...idempotent })).length;
47
- }),
48
- upsert: (async function (collection, query, data, rollback) {
49
- await this.insert(collection, query, data.$setOnInsert);
50
- return await this.update(collection, query, data, rollback);
51
- }),
52
- cleanup: (async function (collection, query) {
53
- await Elabase.update(this.meta, collection, query, { $unset: { [this.txn]: true } });
54
- }),
55
- finish: (async function () {
56
- return await Elabase.update(this.meta, "txns", { _id: this.txn }, {}, { upsert: true });
57
- })
58
34
  };
35
+ const idip = {};
36
+ idip.new = async function (meta, txn, blank) {
37
+ state.txn = txn;
38
+ state.meta = meta;
39
+ return blank ? idip : ((await Elabase.query(state.meta, "txns", { _id: state.txn })).length ? false : idip);
40
+ };
41
+ idip.insert = async function (collection, query, data) {
42
+ await Elabase.update(state.meta, collection, query, { $setOnInsert: data }, { upsert: true });
43
+ };
44
+ idip.update = async function (collection, query, data, rollback) {
45
+ let token = { [state.txn]: true };
46
+ let idempotent = { [state.txn]: { $exists: rollback ?? false } };
47
+ let result = await Elabase.update(state.meta, collection, { ...query, ...idempotent }, { ...data, $setOnInsert: undefined, $set: { ...(data.$set ?? {}), ...token } });
48
+ return result.count ? true : (await Elabase.query(state.meta, collection, { ...query, ...idempotent })).length;
49
+ };
50
+ idip.upsert = async function (collection, query, data, rollback) {
51
+ await idip.insert(collection, query, data.$setOnInsert);
52
+ return await idip.update(collection, query, data, rollback);
53
+ };
54
+ idip.cleanup = async function (collection, query) {
55
+ await Elabase.update(state.meta, collection, query, { $unset: { [state.txn]: true } });
56
+ };
57
+ idip.finish = async function () {
58
+ return await Elabase.update(state.meta, "txns", { _id: state.txn }, {}, { upsert: true });
59
+ };
60
+ return idip;
59
61
  };
60
62
  export const idip = async (message, callback, cleanup) => {
61
63
  if (!callback)
@@ -93,7 +95,7 @@ export const insertKeys = (message, meta) => {
93
95
  ...(meta?.outlet ? { "outlet": meta.outlet } : {}),
94
96
  };
95
97
  };
96
- export const updateKeys = message => {
98
+ export const updateKeys = (message) => {
97
99
  return {
98
100
  "updated": message.date,
99
101
  "updatedBy": message.meta.staff,
@@ -1,6 +1,5 @@
1
1
  // @ts-ignore
2
2
  import axios from 'axios';
3
- // import {curly} from 'node-libcurl'
4
3
  function hashStr(str) {
5
4
  let hash = 0;
6
5
  for (let i = 0; i < str.length; i++) {
@@ -43,7 +43,7 @@ function deepFreeze(obj) {
43
43
  }
44
44
  return obj;
45
45
  }
46
- class DipMeta {
46
+ export class DipMeta {
47
47
  constructor(obj) {
48
48
  Object.assign(this, deepFreeze(obj));
49
49
  Object.freeze(this);
@@ -2,7 +2,9 @@
2
2
  import { Kafka, logLevel as KafkaLogLevel, CompressionTypes as KafkaCompressionTypes } from 'kafkajs';
3
3
  export const logLevel = KafkaLogLevel;
4
4
  export const CompressionTypes = KafkaCompressionTypes;
5
- let kafka, producer, admin;
5
+ let kafka;
6
+ let producer;
7
+ let admin;
6
8
  let appName;
7
9
  let topicPrefix = '';
8
10
  export const start = (arg) => {
@@ -37,16 +39,17 @@ const start_consumer = async function (topic, groupId, callback) {
37
39
  consumers.push(consumer);
38
40
  await consumer.connect();
39
41
  await consumer.subscribe({ topic: topic, fromBeginning: true });
42
+ const fn = callback;
40
43
  await consumer.run({
41
44
  autoCommit: false,
42
45
  eachMessage: async ({ topic, partition, message }) => {
43
- // callback(topic, message.value.toString(), partition) // toString() returns array so won't parse if json.
46
+ // fn(topic, message.value.toString(), partition) // toString() returns array so won't parse if json.
44
47
  let success;
45
48
  try {
46
- success = await callback(topic, JSON.parse(message.value), partition);
49
+ success = await fn(topic, JSON.parse(message.value), partition);
47
50
  }
48
51
  catch (ex) {
49
- success = await callback(topic, message.value, partition);
52
+ success = await fn(topic, message.value, partition);
50
53
  }
51
54
  await consumer.commitOffsets([{ topic, partition, offset: (Number(message.offset) + 1).toString() }]);
52
55
  },
@@ -4,14 +4,17 @@ import axios from 'axios';
4
4
  // @ts-ignore
5
5
  import { createClient } from 'redis';
6
6
  const url = process.env.REDIS_URL || "redis://localhost:6380";
7
- // url: 'redis://alice:foobared@localhost:6380'
8
- let consumer, publisher;
7
+ let consumer;
8
+ let publisher;
9
9
  let users_pool = {};
10
10
  async function disconnect(user, uid) {
11
11
  if (!users_pool[user])
12
12
  return;
13
- let [{ res }] = users_pool[user].reqres.filter(item => item.uid === uid).concat([{}]);
14
- res.end();
13
+ let res_items = users_pool[user].reqres.filter(item => item.uid === uid);
14
+ if (res_items) {
15
+ const res = res_items[0].res;
16
+ res.end();
17
+ }
15
18
  if (users_pool[user].listeners[uid]) {
16
19
  if (users_pool[user].listeners.count == 1)
17
20
  await consumer.unsubscribe(user, users_pool[user].listeners[uid]);
@@ -8,7 +8,7 @@ const REFRESH_TOKEN_SECRET = process.env.JWT_REFRESH_TOKEN_SECRET || "MY_SECRET_
8
8
  const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN";
9
9
  const NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN_SECRET = process.env.NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN_PUBLIC_KEY;
10
10
  let urlsAllowed = [];
11
- export var ALLOWED_URLS = [];
11
+ export let ALLOWED_URLS = [];
12
12
  export const start = (expressApp, allowedUrls) => {
13
13
  urlsAllowed = ["/refreshToken", "/login"].concat(allowedUrls ?? []);
14
14
  ALLOWED_URLS = urlsAllowed;
@@ -54,7 +54,7 @@ export const start = (expressApp, allowedUrls) => {
54
54
  throw null;
55
55
  }
56
56
  catch (error) {
57
- return res.status(401).json({ message: error?.message === "Access Denied" ? error.message : 'Unauthorized' });
57
+ res.status(401).json({ message: error?.message === "Access Denied" ? "Access Denied" : 'Unauthorized' });
58
58
  }
59
59
  });
60
60
  app.post("/refreshToken", (req, res) => {
package/libs/auth.ts CHANGED
@@ -7,12 +7,51 @@ import * as Dip from './dip.ts'
7
7
  import * as Utils from './utils.ts'
8
8
  import * as Session from './session.ts'
9
9
 
10
- let validateFn, validateErrMessage
11
- export const validate = (callback, errMessage) => {
10
+
11
+ type AuthRequest = {
12
+ body: {
13
+ mob?: string
14
+ phone?: string
15
+ code?: string
16
+ otp?: string
17
+ clientId?: string
18
+ app?: string
19
+ hash?: string
20
+ userId?: string
21
+ data?: Record<string, unknown>
22
+ }
23
+ }
24
+
25
+ type AuthResponse = {
26
+ status: (code: number) => AuthResponse
27
+ json: (body: unknown) => void
28
+ }
29
+
30
+ type AuthApp = {
31
+ post: (
32
+ path: string,
33
+ handler: (req: AuthRequest, res: AuthResponse) => Promise<void>
34
+ ) => void
35
+ }
36
+
37
+ type ValidateFunction = (req: AuthRequest) => boolean
38
+
39
+ type SuccessCallback = (
40
+ req: AuthRequest,
41
+ res: AuthResponse,
42
+ data: Record<string, unknown>
43
+ ) => void | Promise<void>
44
+
45
+
46
+
47
+ let validateFn: ValidateFunction | undefined
48
+ let validateErrMessage: string | undefined
49
+
50
+ export const validate = (callback: ValidateFunction, errMessage?: string) => {
12
51
  validateFn = callback
13
52
  }
14
53
 
15
- export const start = (app, successCallback) => {
54
+ export const start = (app: AuthApp, successCallback?: SuccessCallback) => {
16
55
  app.post("/login", async (req, res) => {
17
56
  if (validateFn && !validateFn(req)) {
18
57
  res.status(401).json({ mode: 'login', success: false, info: validateErrMessage ?? 'Validation Failed', message: 'Login Server Error' })
@@ -21,7 +60,7 @@ export const start = (app, successCallback) => {
21
60
  try {
22
61
  let login = await attemptLogin(req, res)
23
62
  if (login.mode === 'verify' && login.success) {
24
- let tokens = Session.generateAccessToken(login.userId, req.body.clientId)
63
+ let tokens = Session.generateAccessToken(login.userId, req.body.clientId ?? '')
25
64
  let response = {...login, tokens}
26
65
  if (successCallback)
27
66
  await successCallback(req, res, response)
@@ -35,12 +74,12 @@ export const start = (app, successCallback) => {
35
74
  })
36
75
  }
37
76
 
38
- async function attemptLogin(req, res) {
77
+ async function attemptLogin(req: AuthRequest, res: AuthResponse) {
39
78
  let meta = {company: "GLOBAL", outlet: "GLOBAL"}
40
79
 
41
80
  let expiry = 300000
42
- let userMob = req.body.mob ?? req.body.phone
43
- const code = req.body.code
81
+ let userMob = req.body.mob ?? req.body.phone ?? ''
82
+ const code = req.body.code ?? ''
44
83
  let mob = userMob.endsWith('123456789') ? '0123456789' : Utils.parseMob(userMob, code)
45
84
  let time = new Date().getTime()
46
85
  let collection = (req.body.app ? req.body.app + '.' : '') + "auth.login"
@@ -73,7 +112,7 @@ async function attemptLogin(req, res) {
73
112
  }
74
113
 
75
114
 
76
- async function sendOtp(mob, otp, app, hash) {
115
+ async function sendOtp(mob: string, otp: string, app?: string, hash?: string) {
77
116
  if (process.env.LOGIN_OTP_DISABLE) {
78
117
  console.log(otp);
79
118
  return true
@@ -81,7 +120,7 @@ async function sendOtp(mob, otp, app, hash) {
81
120
  try {
82
121
  if (app && hash)
83
122
  app = encodeURIComponent(`${app} (#${hash})`) // Of the form '<app> (#<hash>)' for better readability. Note: Axios works with SlypBusiness%20(%23<HASH>) but Curl requires SlypBusiness%20%28%23<HASH>%29
84
- let api = process.env.SMS_API.replace(':phone', mob).replace(':otp', otp).replace(':app', app ?? 'app')
123
+ let api = process.env.SMS_API!.replace(':phone', mob).replace(':otp', otp).replace(':app', app ?? 'app')
85
124
  await axios.get(api)
86
125
  return true
87
126
  } catch {
@@ -9,6 +9,11 @@ import compression from 'compression'
9
9
  // Regarding Client compressing data send to server, I think if server has `Content-Encoding: gzip` set, then client might compress before sending. But not sure. Need to check.
10
10
  // Http Stream or ServerSideEvents require res.flush() after res.write(). res.flush function is added by the express compression middleware
11
11
 
12
- export const start = app => {
12
+
13
+ type ExpressApp = {
14
+ use: (fn: unknown) => unknown
15
+ }
16
+
17
+ export const start = (app: ExpressApp) => {
13
18
  app.use(Utils.excludeMiddleware(compression({threshold: 1024}))) // Response above 1024 bytes should be compressed. Default is also 1KB
14
19
  }