@appsemble/lang-sdk 0.36.3-test.6 → 0.36.4
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/examples.d.ts +3 -2
- package/examples.js +262 -0
- package/package.json +9 -9
- package/reference-schemas/remappers/arrays.js +102 -0
- package/reference-schemas/remappers/conditionals.js +39 -3
- package/reference-schemas/remappers/data.js +22 -0
- package/reference-schemas/remappers/dates.js +47 -0
- package/remap.js +220 -1
- package/types/member.d.ts +4 -0
- package/types/remapper.d.ts +197 -0
package/examples.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ export interface RemapperExample {
|
|
|
4
4
|
input: unknown;
|
|
5
5
|
remapper: unknown;
|
|
6
6
|
result: unknown;
|
|
7
|
+
history?: unknown[];
|
|
7
8
|
skip?: boolean;
|
|
8
9
|
}
|
|
9
|
-
type CustomRemapperKeys = 'app.id' | 'app.locale' | 'app.url' | 'array.map.1' | 'if.else' | 'if.then' | 'None';
|
|
10
|
+
type CustomRemapperKeys = 'app.id' | 'app.locale' | 'app.url' | 'array.map.1' | 'array.range.1' | 'array.range.map' | 'array.range' | 'array.sort.by' | 'array.sort.desc' | 'array.sort.numeric' | 'date.endOf.quarter' | 'date.endOf.week' | 'date.endOf.weekSun' | 'date.endOf.year' | 'date.endOf' | 'date.set' | 'date.startOf.quarter' | 'date.startOf.week' | 'date.startOf.weekSun' | 'date.startOf.year' | 'date.startOf' | 'if.else' | 'if.then' | 'None';
|
|
10
11
|
export type RemapperExampleKeys = CustomRemapperKeys | Exclude<keyof Remappers, 'app' | 'if'>;
|
|
11
12
|
export declare const examples: Record<RemapperExampleKeys, RemapperExample>;
|
|
12
13
|
/**
|
|
@@ -19,5 +20,5 @@ export declare function schemaExample(remapper: keyof typeof examples, options?:
|
|
|
19
20
|
result?: 'inline' | 'pretty';
|
|
20
21
|
exclude?: ('input' | 'remapper' | 'result')[];
|
|
21
22
|
}): string;
|
|
22
|
-
export declare function createExampleContext(url: URL, lang: string, userInfo?: AppMemberInfo, history?: []): RemapperContext;
|
|
23
|
+
export declare function createExampleContext(url: URL, lang: string, userInfo?: AppMemberInfo, history?: unknown[]): RemapperContext;
|
|
23
24
|
export {};
|
package/examples.js
CHANGED
|
@@ -216,6 +216,42 @@ export const examples = {
|
|
|
216
216
|
},
|
|
217
217
|
result: "id eq '5' or id eq '6' or id eq '7'",
|
|
218
218
|
},
|
|
219
|
+
'array.groupBy': {
|
|
220
|
+
input: [
|
|
221
|
+
{ name: 'Alice', department: 'Engineering' },
|
|
222
|
+
{ name: 'Bob', department: 'Sales' },
|
|
223
|
+
{ name: 'Charlie', department: 'Engineering' },
|
|
224
|
+
],
|
|
225
|
+
remapper: {
|
|
226
|
+
'array.groupBy': 'department',
|
|
227
|
+
},
|
|
228
|
+
result: [
|
|
229
|
+
{
|
|
230
|
+
key: 'Engineering',
|
|
231
|
+
items: [
|
|
232
|
+
{ name: 'Alice', department: 'Engineering' },
|
|
233
|
+
{ name: 'Charlie', department: 'Engineering' },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
key: 'Sales',
|
|
238
|
+
items: [{ name: 'Bob', department: 'Sales' }],
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
'array.toObject': {
|
|
243
|
+
input: [
|
|
244
|
+
{ key: 'Engineering', items: ['Alice', 'Charlie'] },
|
|
245
|
+
{ key: 'Sales', items: ['Bob'] },
|
|
246
|
+
],
|
|
247
|
+
remapper: {
|
|
248
|
+
'array.toObject': { key: { prop: 'key' }, value: { prop: 'items' } },
|
|
249
|
+
},
|
|
250
|
+
result: {
|
|
251
|
+
Engineering: ['Alice', 'Charlie'],
|
|
252
|
+
Sales: ['Bob'],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
219
255
|
'array.map': {
|
|
220
256
|
input: [
|
|
221
257
|
{
|
|
@@ -300,6 +336,44 @@ export const examples = {
|
|
|
300
336
|
},
|
|
301
337
|
],
|
|
302
338
|
},
|
|
339
|
+
'array.range': {
|
|
340
|
+
input: null,
|
|
341
|
+
remapper: {
|
|
342
|
+
'array.range': 4,
|
|
343
|
+
},
|
|
344
|
+
result: [0, 1, 2, 3],
|
|
345
|
+
},
|
|
346
|
+
'array.range.1': {
|
|
347
|
+
input: 5,
|
|
348
|
+
remapper: {
|
|
349
|
+
'array.range': { root: null },
|
|
350
|
+
},
|
|
351
|
+
result: [0, 1, 2, 3, 4],
|
|
352
|
+
},
|
|
353
|
+
'array.range.map': {
|
|
354
|
+
input: 3,
|
|
355
|
+
remapper: [
|
|
356
|
+
{ 'array.range': { root: null } },
|
|
357
|
+
{
|
|
358
|
+
'array.map': {
|
|
359
|
+
'object.from': {
|
|
360
|
+
index: { array: 'item' },
|
|
361
|
+
message: {
|
|
362
|
+
'string.format': {
|
|
363
|
+
template: 'Item {i}',
|
|
364
|
+
values: { i: { array: 'item' } },
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
result: [
|
|
372
|
+
{ index: 0, message: 'Item 0' },
|
|
373
|
+
{ index: 1, message: 'Item 1' },
|
|
374
|
+
{ index: 2, message: 'Item 2' },
|
|
375
|
+
],
|
|
376
|
+
},
|
|
303
377
|
'array.omit': {
|
|
304
378
|
input: [
|
|
305
379
|
{
|
|
@@ -337,6 +411,42 @@ export const examples = {
|
|
|
337
411
|
},
|
|
338
412
|
],
|
|
339
413
|
},
|
|
414
|
+
'array.sort': {
|
|
415
|
+
input: [3, 1, 4, 1, 5, 9, 2, 6],
|
|
416
|
+
remapper: {
|
|
417
|
+
'array.sort': null,
|
|
418
|
+
},
|
|
419
|
+
result: [1, 1, 2, 3, 4, 5, 6, 9],
|
|
420
|
+
},
|
|
421
|
+
'array.sort.by': {
|
|
422
|
+
input: [
|
|
423
|
+
{ name: 'Charlie', age: 30 },
|
|
424
|
+
{ name: 'Alice', age: 25 },
|
|
425
|
+
{ name: 'Bob', age: 35 },
|
|
426
|
+
],
|
|
427
|
+
remapper: {
|
|
428
|
+
'array.sort': 'name',
|
|
429
|
+
},
|
|
430
|
+
result: [
|
|
431
|
+
{ name: 'Alice', age: 25 },
|
|
432
|
+
{ name: 'Bob', age: 35 },
|
|
433
|
+
{ name: 'Charlie', age: 30 },
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
'array.sort.desc': {
|
|
437
|
+
input: [3, 1, 4, 1, 5, 9, 2, 6],
|
|
438
|
+
remapper: {
|
|
439
|
+
'array.sort': { descending: true },
|
|
440
|
+
},
|
|
441
|
+
result: [9, 6, 5, 4, 3, 2, 1, 1],
|
|
442
|
+
},
|
|
443
|
+
'array.sort.numeric': {
|
|
444
|
+
input: ['10', '2', '1', '20'],
|
|
445
|
+
remapper: {
|
|
446
|
+
'array.sort': { strategy: 'numeric' },
|
|
447
|
+
},
|
|
448
|
+
result: ['1', '2', '10', '20'],
|
|
449
|
+
},
|
|
340
450
|
'array.unique': {
|
|
341
451
|
input: [1, 1, 2, 3],
|
|
342
452
|
remapper: {
|
|
@@ -415,6 +525,71 @@ export const examples = {
|
|
|
415
525
|
result: 'Tue Feb 11 2014 00:00:00',
|
|
416
526
|
skip: true,
|
|
417
527
|
},
|
|
528
|
+
'date.startOf': {
|
|
529
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
530
|
+
remapper: { 'date.startOf': 'month' },
|
|
531
|
+
result: '2025-11-01T00:00:00.000Z',
|
|
532
|
+
skip: true,
|
|
533
|
+
},
|
|
534
|
+
'date.startOf.year': {
|
|
535
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
536
|
+
remapper: { 'date.startOf': 'year' },
|
|
537
|
+
result: '2025-01-01T00:00:00.000Z',
|
|
538
|
+
skip: true,
|
|
539
|
+
},
|
|
540
|
+
'date.startOf.quarter': {
|
|
541
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
542
|
+
remapper: { 'date.startOf': 'quarter' },
|
|
543
|
+
result: '2025-10-01T00:00:00.000Z',
|
|
544
|
+
skip: true,
|
|
545
|
+
},
|
|
546
|
+
'date.startOf.week': {
|
|
547
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
548
|
+
remapper: { 'date.startOf': 'week' },
|
|
549
|
+
result: '2025-11-17T00:00:00.000Z',
|
|
550
|
+
skip: true,
|
|
551
|
+
},
|
|
552
|
+
'date.startOf.weekSun': {
|
|
553
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
554
|
+
remapper: { 'date.startOf': 'weekSun' },
|
|
555
|
+
result: '2025-11-16T00:00:00.000Z',
|
|
556
|
+
skip: true,
|
|
557
|
+
},
|
|
558
|
+
'date.endOf': {
|
|
559
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
560
|
+
remapper: { 'date.endOf': 'month' },
|
|
561
|
+
result: '2025-11-30T23:59:59.999Z',
|
|
562
|
+
skip: true,
|
|
563
|
+
},
|
|
564
|
+
'date.endOf.year': {
|
|
565
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
566
|
+
remapper: { 'date.endOf': 'year' },
|
|
567
|
+
result: '2025-12-31T23:59:59.999Z',
|
|
568
|
+
skip: true,
|
|
569
|
+
},
|
|
570
|
+
'date.endOf.quarter': {
|
|
571
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
572
|
+
remapper: { 'date.endOf': 'quarter' },
|
|
573
|
+
result: '2025-12-31T23:59:59.999Z',
|
|
574
|
+
skip: true,
|
|
575
|
+
},
|
|
576
|
+
'date.endOf.week': {
|
|
577
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
578
|
+
remapper: { 'date.endOf': 'week' },
|
|
579
|
+
result: '2025-11-23T23:59:59.999Z',
|
|
580
|
+
skip: true,
|
|
581
|
+
},
|
|
582
|
+
'date.endOf.weekSun': {
|
|
583
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
584
|
+
remapper: { 'date.endOf': 'weekSun' },
|
|
585
|
+
result: '2025-11-22T23:59:59.999Z',
|
|
586
|
+
skip: true,
|
|
587
|
+
},
|
|
588
|
+
'date.set': {
|
|
589
|
+
input: '2025-11-21T12:00:00.000Z',
|
|
590
|
+
remapper: { 'date.set': { year: 2026, month: 1, day: 1 } },
|
|
591
|
+
result: '2026-01-01T12:00:00.000Z',
|
|
592
|
+
},
|
|
418
593
|
equals: {
|
|
419
594
|
input: { inputValue: 'example', expectedValue: 'example' },
|
|
420
595
|
remapper: {
|
|
@@ -433,6 +608,11 @@ export const examples = {
|
|
|
433
608
|
remapper: { gt: [{ prop: 'stock' }, 5] },
|
|
434
609
|
result: true,
|
|
435
610
|
},
|
|
611
|
+
gte: {
|
|
612
|
+
input: { stock: 5 },
|
|
613
|
+
remapper: { gte: [{ prop: 'stock' }, 5] },
|
|
614
|
+
result: true,
|
|
615
|
+
},
|
|
436
616
|
history: {
|
|
437
617
|
input: null,
|
|
438
618
|
remapper: {},
|
|
@@ -500,6 +680,11 @@ export const examples = {
|
|
|
500
680
|
remapper: { lt: [{ prop: 'stock' }, 5] },
|
|
501
681
|
result: true,
|
|
502
682
|
},
|
|
683
|
+
lte: {
|
|
684
|
+
input: { stock: 5 },
|
|
685
|
+
remapper: { lte: [{ prop: 'stock' }, 5] },
|
|
686
|
+
result: true,
|
|
687
|
+
},
|
|
503
688
|
match: {
|
|
504
689
|
input: { Gem: 'Ruby' },
|
|
505
690
|
remapper: {
|
|
@@ -929,6 +1114,83 @@ export const examples = {
|
|
|
929
1114
|
},
|
|
930
1115
|
result: [true, true, true, false, false],
|
|
931
1116
|
},
|
|
1117
|
+
focus: {
|
|
1118
|
+
input: null,
|
|
1119
|
+
history: [
|
|
1120
|
+
[
|
|
1121
|
+
{ id: 'customer1', name: 'Customer 1' },
|
|
1122
|
+
{ id: 'customer2', name: 'Customer 2' },
|
|
1123
|
+
{ id: 'customer3', name: 'Customer 3' },
|
|
1124
|
+
],
|
|
1125
|
+
[
|
|
1126
|
+
{ id: 'orderA', customerId: 'customer1', value: 'Order A for Customer 1' },
|
|
1127
|
+
{ id: 'orderB', customerId: 'customer2', value: 'Order B for Customer 2' },
|
|
1128
|
+
{ id: 'orderC', customerId: 'customer1', value: 'Order C for Customer 1' },
|
|
1129
|
+
{ id: 'orderD', customerId: 'customer3', value: 'Order D for Customer 3' },
|
|
1130
|
+
{ id: 'orderE', customerId: 'customer1', value: 'Order E for Customer 1' },
|
|
1131
|
+
{ id: 'orderF', customerId: 'customer2', value: 'Order F for Customer 2' },
|
|
1132
|
+
],
|
|
1133
|
+
],
|
|
1134
|
+
remapper: [
|
|
1135
|
+
{ history: 0 },
|
|
1136
|
+
{
|
|
1137
|
+
'array.map': {
|
|
1138
|
+
focus: {
|
|
1139
|
+
on: {
|
|
1140
|
+
'object.from': {
|
|
1141
|
+
currentCustomer: { array: 'item' },
|
|
1142
|
+
allOrders: { history: 1 },
|
|
1143
|
+
},
|
|
1144
|
+
},
|
|
1145
|
+
do: {
|
|
1146
|
+
'object.from': {
|
|
1147
|
+
id: { prop: 'id' },
|
|
1148
|
+
name: { prop: 'name' },
|
|
1149
|
+
associatedOrders: [
|
|
1150
|
+
{ root: null },
|
|
1151
|
+
{ prop: 'allOrders' },
|
|
1152
|
+
{
|
|
1153
|
+
'array.filter': {
|
|
1154
|
+
equals: [
|
|
1155
|
+
{ prop: 'customerId' },
|
|
1156
|
+
[{ root: null }, { prop: 'currentCustomer' }, { prop: 'id' }],
|
|
1157
|
+
],
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
],
|
|
1161
|
+
},
|
|
1162
|
+
},
|
|
1163
|
+
},
|
|
1164
|
+
},
|
|
1165
|
+
},
|
|
1166
|
+
],
|
|
1167
|
+
result: [
|
|
1168
|
+
{
|
|
1169
|
+
id: 'customer1',
|
|
1170
|
+
name: 'Customer 1',
|
|
1171
|
+
associatedOrders: [
|
|
1172
|
+
{ id: 'orderA', customerId: 'customer1', value: 'Order A for Customer 1' },
|
|
1173
|
+
{ id: 'orderC', customerId: 'customer1', value: 'Order C for Customer 1' },
|
|
1174
|
+
{ id: 'orderE', customerId: 'customer1', value: 'Order E for Customer 1' },
|
|
1175
|
+
],
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
id: 'customer2',
|
|
1179
|
+
name: 'Customer 2',
|
|
1180
|
+
associatedOrders: [
|
|
1181
|
+
{ id: 'orderB', customerId: 'customer2', value: 'Order B for Customer 2' },
|
|
1182
|
+
{ id: 'orderF', customerId: 'customer2', value: 'Order F for Customer 2' },
|
|
1183
|
+
],
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
id: 'customer3',
|
|
1187
|
+
name: 'Customer 3',
|
|
1188
|
+
associatedOrders: [
|
|
1189
|
+
{ id: 'orderD', customerId: 'customer3', value: 'Order D for Customer 3' },
|
|
1190
|
+
],
|
|
1191
|
+
},
|
|
1192
|
+
],
|
|
1193
|
+
},
|
|
932
1194
|
maths: {
|
|
933
1195
|
input: { version: 0 },
|
|
934
1196
|
remapper: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/lang-sdk",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.4",
|
|
4
4
|
"description": "Language SDK for Appsemble",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -39,21 +39,21 @@
|
|
|
39
39
|
"test": "vitest"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"openapi-types": "^12.0.0",
|
|
43
42
|
"@odata/parser": "^0.2.14",
|
|
43
|
+
"cron-parser": "^4.0.0",
|
|
44
44
|
"date-fns": "^2.0.0",
|
|
45
|
+
"date-fns-tz": "^2.0.1",
|
|
45
46
|
"fast-deep-equal": "^3.0.0",
|
|
47
|
+
"fast-xml-parser": "^5.0.8",
|
|
46
48
|
"ics": "^3.0.0",
|
|
47
49
|
"intl-messageformat": "^11.0.0",
|
|
48
|
-
"
|
|
49
|
-
"fast-xml-parser": "^5.0.8",
|
|
50
|
-
"vitest": "^2.0.0",
|
|
51
|
-
"cron-parser": "^4.0.0",
|
|
50
|
+
"jsonschema": "~1.4.1",
|
|
52
51
|
"language-tags": "^1.0.0",
|
|
52
|
+
"lcm": "^0.0.3",
|
|
53
|
+
"openapi-types": "^12.0.0",
|
|
54
|
+
"parse-duration": "^1.0.0",
|
|
53
55
|
"type-fest": "^4.0.0",
|
|
54
|
-
"
|
|
55
|
-
"yaml": "^2.0.0",
|
|
56
|
-
"lcm": "^0.0.3"
|
|
56
|
+
"yaml": "^2.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/language-tags": "1.0.4",
|
|
@@ -25,6 +25,21 @@ return the full object with the data we want in the \`then\` section. It’s als
|
|
|
25
25
|
This results in an array consisting of \`null\` values and objects with actual data in them. To solve
|
|
26
26
|
this problem, we use the \`null.strip\` remapper to remove any null values which results in a clean
|
|
27
27
|
array.
|
|
28
|
+
`,
|
|
29
|
+
},
|
|
30
|
+
'array.range': {
|
|
31
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
32
|
+
description: `
|
|
33
|
+
Creates an array of numbers from 0 to N-1, where N is the resolved value of the remapper.
|
|
34
|
+
This is useful for creating a dynamically-sized array.
|
|
35
|
+
|
|
36
|
+
If the resolved value is not a non-negative integer, an empty array is returned.
|
|
37
|
+
|
|
38
|
+
${schemaExample('array.range')}
|
|
39
|
+
|
|
40
|
+
It's particularly powerful when combined with \`array.map\` to generate a list of arbitrary elements.
|
|
41
|
+
|
|
42
|
+
${schemaExample('array.range.map', { input: 'pretty' })}
|
|
28
43
|
`,
|
|
29
44
|
},
|
|
30
45
|
'array.join': {
|
|
@@ -34,6 +49,41 @@ array.
|
|
|
34
49
|
If the input is not an array, input is returned without any modifications.
|
|
35
50
|
|
|
36
51
|
${schemaExample('array.join')}
|
|
52
|
+
`,
|
|
53
|
+
},
|
|
54
|
+
'array.groupBy': {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: `
|
|
57
|
+
Groups an array of objects by a common property value.
|
|
58
|
+
|
|
59
|
+
Returns an array of group objects, each containing a \`key\` (the grouped property value)
|
|
60
|
+
and \`items\` (array of objects with that property value).
|
|
61
|
+
|
|
62
|
+
If the input is not an array, returns an empty array.
|
|
63
|
+
|
|
64
|
+
${schemaExample('array.groupBy')}
|
|
65
|
+
|
|
66
|
+
The groups preserve the order of first occurrence of each key value.
|
|
67
|
+
`,
|
|
68
|
+
},
|
|
69
|
+
'array.toObject': {
|
|
70
|
+
type: 'object',
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
required: ['key', 'value'],
|
|
73
|
+
properties: {
|
|
74
|
+
key: { $ref: '#/components/schemas/RemapperDefinition' },
|
|
75
|
+
value: { $ref: '#/components/schemas/RemapperDefinition' },
|
|
76
|
+
},
|
|
77
|
+
description: `
|
|
78
|
+
Converts an array of objects into a single object using specified key and value remappers.
|
|
79
|
+
|
|
80
|
+
For each item in the array, the \`key\` remapper determines the property name and the \`value\`
|
|
81
|
+
remapper determines the property value in the resulting object.
|
|
82
|
+
|
|
83
|
+
If the input is not an array, returns an empty object.
|
|
84
|
+
If multiple items produce the same key, later items overwrite earlier ones.
|
|
85
|
+
|
|
86
|
+
${schemaExample('array.toObject')}
|
|
37
87
|
`,
|
|
38
88
|
},
|
|
39
89
|
'array.unique': {
|
|
@@ -300,6 +350,58 @@ ${schemaExample('array.flatten', { input: 'pretty', exclude: ['remapper'] })}
|
|
|
300
350
|
description: `
|
|
301
351
|
Checks if the input array includes the provided item.
|
|
302
352
|
${schemaExample('array.contains')}
|
|
353
|
+
`,
|
|
354
|
+
},
|
|
355
|
+
'array.sort': {
|
|
356
|
+
oneOf: [
|
|
357
|
+
{ type: 'string' },
|
|
358
|
+
{
|
|
359
|
+
type: 'object',
|
|
360
|
+
nullable: true,
|
|
361
|
+
additionalProperties: false,
|
|
362
|
+
properties: {
|
|
363
|
+
by: { type: 'string' },
|
|
364
|
+
descending: { type: 'boolean', default: false },
|
|
365
|
+
strategy: {
|
|
366
|
+
type: 'string',
|
|
367
|
+
enum: ['infer', 'numeric', 'lexicographic', 'date'],
|
|
368
|
+
default: 'infer',
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
description: `
|
|
374
|
+
Sorts an array of items.
|
|
375
|
+
|
|
376
|
+
Can sort by a property (for arrays of objects) or by the items themselves (for primitive arrays).
|
|
377
|
+
Supports ascending and descending order, and handles numbers, strings, and dates.
|
|
378
|
+
|
|
379
|
+
When a string is provided, it's used as the property name to sort by (ascending).
|
|
380
|
+
|
|
381
|
+
**Sorting strategy (\`strategy\` option):**
|
|
382
|
+
|
|
383
|
+
- \`infer\` (default): Determines comparison type from the first non-null value.
|
|
384
|
+
Numbers use numeric comparison, Dates use timestamp comparison, everything else
|
|
385
|
+
uses lexicographic (string) comparison. Mixed types fall back to lexicographic.
|
|
386
|
+
- \`numeric\`: Coerces values to numbers. Non-numeric values (NaN) are pushed to the end.
|
|
387
|
+
- \`lexicographic\`: Converts values to strings and uses \`localeCompare()\`.
|
|
388
|
+
- \`date\`: Parses values as ISO dates and compares timestamps. Invalid dates are pushed to the end.
|
|
389
|
+
|
|
390
|
+
Nullish values (null/undefined) are always pushed to the end regardless of strategy.
|
|
391
|
+
|
|
392
|
+
${schemaExample('array.sort')}
|
|
393
|
+
|
|
394
|
+
For arrays of objects, you can simply use a string to sort by that property:
|
|
395
|
+
|
|
396
|
+
${schemaExample('array.sort.by')}
|
|
397
|
+
|
|
398
|
+
Use \`descending: true\` to sort in descending order:
|
|
399
|
+
|
|
400
|
+
${schemaExample('array.sort.desc')}
|
|
401
|
+
|
|
402
|
+
Use \`strategy: numeric\` to force numeric comparison (useful for string numbers):
|
|
403
|
+
|
|
404
|
+
${schemaExample('array.sort.numeric')}
|
|
303
405
|
`,
|
|
304
406
|
},
|
|
305
407
|
};
|
|
@@ -55,6 +55,24 @@ Returns \`true\` if the first entry is greater than the second entry.
|
|
|
55
55
|
For example, if \`stock\` is more than 5 here, it returns \`true\`.
|
|
56
56
|
|
|
57
57
|
${schemaExample('gt')}
|
|
58
|
+
`,
|
|
59
|
+
minItems: 2,
|
|
60
|
+
maxItems: 2,
|
|
61
|
+
items: {
|
|
62
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
gte: {
|
|
66
|
+
type: 'array',
|
|
67
|
+
description: `**(gte = Greater than or equal)**
|
|
68
|
+
|
|
69
|
+
Compare the first computed remapper value with the second computed remapper value.
|
|
70
|
+
|
|
71
|
+
Returns \`true\` if the first entry is greater than or equal to the second entry.
|
|
72
|
+
|
|
73
|
+
For example, if \`stock\` is 5 or more here, it returns \`true\`.
|
|
74
|
+
|
|
75
|
+
${schemaExample('gte')}
|
|
58
76
|
`,
|
|
59
77
|
minItems: 2,
|
|
60
78
|
maxItems: 2,
|
|
@@ -64,15 +82,33 @@ ${schemaExample('gt')}
|
|
|
64
82
|
},
|
|
65
83
|
lt: {
|
|
66
84
|
type: 'array',
|
|
67
|
-
description: `**(lt =
|
|
85
|
+
description: `**(lt = Less than)**
|
|
68
86
|
|
|
69
87
|
Compare the first computed remapper value with the second computed remapper value.
|
|
70
88
|
|
|
71
|
-
Returns \`true\` if the first entry is
|
|
89
|
+
Returns \`true\` if the first entry is less than the second entry.
|
|
72
90
|
|
|
73
91
|
For example, if \`stock\` is less than 5 here, it returns \`true\`.
|
|
74
92
|
|
|
75
|
-
${schemaExample('
|
|
93
|
+
${schemaExample('lt')}
|
|
94
|
+
`,
|
|
95
|
+
minItems: 2,
|
|
96
|
+
maxItems: 2,
|
|
97
|
+
items: {
|
|
98
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
lte: {
|
|
102
|
+
type: 'array',
|
|
103
|
+
description: `**(lte = Less than or equal)**
|
|
104
|
+
|
|
105
|
+
Compare the first computed remapper value with the second computed remapper value.
|
|
106
|
+
|
|
107
|
+
Returns \`true\` if the first entry is less than or equal to the second entry.
|
|
108
|
+
|
|
109
|
+
For example, if \`stock\` is 5 or less here, it returns \`true\`.
|
|
110
|
+
|
|
111
|
+
${schemaExample('lte')}
|
|
76
112
|
`,
|
|
77
113
|
minItems: 2,
|
|
78
114
|
maxItems: 2,
|
|
@@ -410,5 +410,27 @@ ${schemaExample('slice')}`,
|
|
|
410
410
|
{ type: 'number' },
|
|
411
411
|
],
|
|
412
412
|
},
|
|
413
|
+
focus: {
|
|
414
|
+
type: 'object',
|
|
415
|
+
description: `
|
|
416
|
+
Executes a remapper chain with a temporary root context.
|
|
417
|
+
|
|
418
|
+
This is useful for solving nested context problems, like performing a
|
|
419
|
+
filter on one list that depends on a value from an outer loop.
|
|
420
|
+
|
|
421
|
+
${schemaExample('focus', { input: 'pretty', result: 'pretty', exclude: [] })}
|
|
422
|
+
`,
|
|
423
|
+
required: ['on', 'do'],
|
|
424
|
+
properties: {
|
|
425
|
+
on: {
|
|
426
|
+
description: 'The data to focus on. The input data will be remapped with this remapper, and the result will be passed as root context to `do`.',
|
|
427
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
428
|
+
},
|
|
429
|
+
do: {
|
|
430
|
+
description: 'The remapper to execute with the focused data as root. The original input will be provided as input, and the result of `on` will be the root context.',
|
|
431
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
},
|
|
413
435
|
};
|
|
414
436
|
//# sourceMappingURL=data.js.map
|
|
@@ -90,5 +90,52 @@ date.parse:
|
|
|
90
90
|
\`\`\`
|
|
91
91
|
`,
|
|
92
92
|
},
|
|
93
|
+
'date.startOf': {
|
|
94
|
+
enum: ['year', 'quarter', 'month', 'week', 'weekSun'],
|
|
95
|
+
description: `Takes a date and returns the start of the specified unit.
|
|
96
|
+
|
|
97
|
+
Supported units:
|
|
98
|
+
- \`year\`: First day of the year
|
|
99
|
+
- \`quarter\`: First day of the quarter
|
|
100
|
+
- \`month\`: First day of the month
|
|
101
|
+
- \`week\`: First day of the week (Monday)
|
|
102
|
+
- \`weekSun\`: First day of the week (Sunday)
|
|
103
|
+
|
|
104
|
+
${schemaExample('date.startOf')}
|
|
105
|
+
`,
|
|
106
|
+
},
|
|
107
|
+
'date.endOf': {
|
|
108
|
+
enum: ['year', 'quarter', 'month', 'week', 'weekSun'],
|
|
109
|
+
description: `Takes a date and returns the end of the specified unit.
|
|
110
|
+
|
|
111
|
+
Supported units:
|
|
112
|
+
- \`year\`: Last moment of the year
|
|
113
|
+
- \`quarter\`: Last moment of the quarter
|
|
114
|
+
- \`month\`: Last moment of the month
|
|
115
|
+
- \`week\`: Last moment of the week (ends Sunday if week starts Monday)
|
|
116
|
+
- \`weekSun\`: Last moment of the week (ends Saturday if week starts Sunday)
|
|
117
|
+
|
|
118
|
+
${schemaExample('date.endOf')}
|
|
119
|
+
`,
|
|
120
|
+
},
|
|
121
|
+
'date.set': {
|
|
122
|
+
type: 'object',
|
|
123
|
+
description: `Sets parts of a date. Only the given parts are changed.
|
|
124
|
+
|
|
125
|
+
Month is 1-indexed (1 = January, 12 = December).
|
|
126
|
+
|
|
127
|
+
${schemaExample('date.set')}`,
|
|
128
|
+
properties: {
|
|
129
|
+
year: {
|
|
130
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
131
|
+
},
|
|
132
|
+
month: {
|
|
133
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
134
|
+
},
|
|
135
|
+
day: {
|
|
136
|
+
$ref: '#/components/schemas/RemapperDefinition',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
93
140
|
};
|
|
94
141
|
//# sourceMappingURL=dates.js.map
|
package/remap.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { filter, literalValues, param } from '@odata/parser';
|
|
2
|
-
import { addMilliseconds, format, parse, parseISO } from 'date-fns';
|
|
2
|
+
import { addMilliseconds, endOfMonth, endOfQuarter, endOfWeek, endOfYear, format, parse, parseISO, set as setDate, startOfMonth, startOfQuarter, startOfWeek, startOfYear, } from 'date-fns';
|
|
3
|
+
import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz';
|
|
3
4
|
import equal from 'fast-deep-equal';
|
|
4
5
|
import { XMLParser } from 'fast-xml-parser';
|
|
5
6
|
import { createEvent } from 'ics';
|
|
@@ -66,6 +67,40 @@ export function remap(remapper, input, context) {
|
|
|
66
67
|
}
|
|
67
68
|
return result;
|
|
68
69
|
}
|
|
70
|
+
// Comparison functions for array.sort
|
|
71
|
+
const compareNumeric = (aVal, bVal) => {
|
|
72
|
+
const aNum = Number(aVal);
|
|
73
|
+
const bNum = Number(bVal);
|
|
74
|
+
const aIsNaN = Number.isNaN(aNum);
|
|
75
|
+
const bIsNaN = Number.isNaN(bNum);
|
|
76
|
+
if (aIsNaN && bIsNaN) {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
if (aIsNaN) {
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
if (bIsNaN) {
|
|
83
|
+
return -1;
|
|
84
|
+
}
|
|
85
|
+
return aNum - bNum;
|
|
86
|
+
};
|
|
87
|
+
const compareLexicographic = (aVal, bVal) => String(aVal).localeCompare(String(bVal));
|
|
88
|
+
const compareDate = (aVal, bVal) => {
|
|
89
|
+
const aDate = aVal instanceof Date ? aVal : parseISO(String(aVal));
|
|
90
|
+
const bDate = bVal instanceof Date ? bVal : parseISO(String(bVal));
|
|
91
|
+
const aInvalid = Number.isNaN(aDate.getTime());
|
|
92
|
+
const bInvalid = Number.isNaN(bDate.getTime());
|
|
93
|
+
if (aInvalid && bInvalid) {
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
if (aInvalid) {
|
|
97
|
+
return 1;
|
|
98
|
+
}
|
|
99
|
+
if (bInvalid) {
|
|
100
|
+
return -1;
|
|
101
|
+
}
|
|
102
|
+
return aDate.getTime() - bDate.getTime();
|
|
103
|
+
};
|
|
69
104
|
/**
|
|
70
105
|
* Implementations of all remappers.
|
|
71
106
|
*
|
|
@@ -150,10 +185,18 @@ const mapperImplementations = {
|
|
|
150
185
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
151
186
|
// @ts-ignore Messed up - 2571 Object is of type 'unknown'.
|
|
152
187
|
remap(left, input, context) > remap(right, input, context),
|
|
188
|
+
gte: ([left, right], input, context) =>
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
190
|
+
// @ts-ignore Messed up - 2571 Object is of type 'unknown'.
|
|
191
|
+
remap(left, input, context) >= remap(right, input, context),
|
|
153
192
|
lt: ([left, right], input, context) =>
|
|
154
193
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
155
194
|
// @ts-ignore Messed up - 2571 Object is of type 'unknown'.
|
|
156
195
|
remap(left, input, context) < remap(right, input, context),
|
|
196
|
+
lte: ([left, right], input, context) =>
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
198
|
+
// @ts-ignore Messed up - 2571 Object is of type 'unknown'.
|
|
199
|
+
remap(left, input, context) <= remap(right, input, context),
|
|
157
200
|
ics(mappers, input, context) {
|
|
158
201
|
let event;
|
|
159
202
|
const mappedStart = remap(mappers.start, input, context);
|
|
@@ -207,6 +250,10 @@ const mapperImplementations = {
|
|
|
207
250
|
const condition = remap(mappers.condition, input, context);
|
|
208
251
|
return remap(condition ? mappers.then : mappers.else, input, context);
|
|
209
252
|
},
|
|
253
|
+
focus({ do: doRemapper, on }, input, context) {
|
|
254
|
+
const newRoot = remap(on, input, context);
|
|
255
|
+
return remap(doRemapper, input, { ...context, root: newRoot });
|
|
256
|
+
},
|
|
210
257
|
match(mappers, input, context) {
|
|
211
258
|
return (remap(mappers.find((mapper) => remap(mapper.case, input, context))?.value ?? null, input, context) ?? null);
|
|
212
259
|
},
|
|
@@ -309,6 +356,13 @@ const mapperImplementations = {
|
|
|
309
356
|
nextItem: input[index + 1],
|
|
310
357
|
},
|
|
311
358
|
})) ?? [],
|
|
359
|
+
'array.range'(countRemapper, input, context) {
|
|
360
|
+
const count = remap(countRemapper, input, context);
|
|
361
|
+
if (typeof count !== 'number' || !Number.isInteger(count) || count < 0) {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
return Array.from({ length: count }, (item, idx) => idx);
|
|
365
|
+
},
|
|
312
366
|
'array.contains'(mapper, input, context) {
|
|
313
367
|
if (!Array.isArray(input)) {
|
|
314
368
|
return false;
|
|
@@ -328,6 +382,92 @@ const mapperImplementations = {
|
|
|
328
382
|
}
|
|
329
383
|
return input.join(separator ?? undefined);
|
|
330
384
|
},
|
|
385
|
+
'array.groupBy'(propertyName, input) {
|
|
386
|
+
if (!Array.isArray(input)) {
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
const groups = new Map();
|
|
390
|
+
for (const item of input) {
|
|
391
|
+
const key = item?.[propertyName];
|
|
392
|
+
if (!groups.has(key)) {
|
|
393
|
+
groups.set(key, []);
|
|
394
|
+
}
|
|
395
|
+
groups.get(key).push(item);
|
|
396
|
+
}
|
|
397
|
+
return [...groups.entries()].map(([key, items]) => ({ key, items }));
|
|
398
|
+
},
|
|
399
|
+
'array.toObject'({ key: keyMapper, value: valueMapper }, input, context) {
|
|
400
|
+
if (!Array.isArray(input)) {
|
|
401
|
+
return {};
|
|
402
|
+
}
|
|
403
|
+
const result = {};
|
|
404
|
+
for (const [index, item] of input.entries()) {
|
|
405
|
+
const itemContext = {
|
|
406
|
+
...context,
|
|
407
|
+
array: {
|
|
408
|
+
index,
|
|
409
|
+
length: input.length,
|
|
410
|
+
item,
|
|
411
|
+
prevItem: input[index - 1],
|
|
412
|
+
nextItem: input[index + 1],
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
const key = remap(keyMapper, item, itemContext);
|
|
416
|
+
const value = remap(valueMapper, item, itemContext);
|
|
417
|
+
if (key != null) {
|
|
418
|
+
result[String(key)] = value;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return result;
|
|
422
|
+
},
|
|
423
|
+
'array.sort'(mapper, input) {
|
|
424
|
+
if (!Array.isArray(input)) {
|
|
425
|
+
return input;
|
|
426
|
+
}
|
|
427
|
+
// Normalize mapper: string becomes { by: string }, null/undefined becomes {}
|
|
428
|
+
const { by, descending = false, strategy = 'infer', } = typeof mapper === 'string'
|
|
429
|
+
? { by: mapper, descending: false, strategy: 'infer' }
|
|
430
|
+
: (mapper ?? {});
|
|
431
|
+
// Extract values for sorting
|
|
432
|
+
const getValue = (item) => by ? item?.[by] : item;
|
|
433
|
+
// Determine effective strategy for 'infer' mode
|
|
434
|
+
let effectiveStrategy = strategy;
|
|
435
|
+
if (strategy === 'infer') {
|
|
436
|
+
// Find first non-null value to determine type
|
|
437
|
+
const firstValue = input.map(getValue).find((v) => v != null);
|
|
438
|
+
if (typeof firstValue === 'number') {
|
|
439
|
+
effectiveStrategy = 'numeric';
|
|
440
|
+
}
|
|
441
|
+
else if (firstValue instanceof Date) {
|
|
442
|
+
effectiveStrategy = 'date';
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
effectiveStrategy = 'lexicographic';
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const compare = effectiveStrategy === 'numeric'
|
|
449
|
+
? compareNumeric
|
|
450
|
+
: effectiveStrategy === 'date'
|
|
451
|
+
? compareDate
|
|
452
|
+
: compareLexicographic;
|
|
453
|
+
const sorted = [...input].sort((a, b) => {
|
|
454
|
+
const aValue = getValue(a);
|
|
455
|
+
const bValue = getValue(b);
|
|
456
|
+
// Handle nullish values - push them to the end
|
|
457
|
+
if (aValue == null && bValue == null) {
|
|
458
|
+
return 0;
|
|
459
|
+
}
|
|
460
|
+
if (aValue == null) {
|
|
461
|
+
return 1;
|
|
462
|
+
}
|
|
463
|
+
if (bValue == null) {
|
|
464
|
+
return -1;
|
|
465
|
+
}
|
|
466
|
+
const comparison = compare(aValue, bValue);
|
|
467
|
+
return descending ? -comparison : comparison;
|
|
468
|
+
});
|
|
469
|
+
return sorted;
|
|
470
|
+
},
|
|
331
471
|
'array.unique'(mapper, input, context) {
|
|
332
472
|
if (!Array.isArray(input)) {
|
|
333
473
|
return input;
|
|
@@ -494,6 +634,85 @@ const mapperImplementations = {
|
|
|
494
634
|
: parseISO(String(input));
|
|
495
635
|
return args ? format(date, args) : date.toJSON();
|
|
496
636
|
},
|
|
637
|
+
'date.startOf'(unit, input) {
|
|
638
|
+
const date = input instanceof Date
|
|
639
|
+
? input
|
|
640
|
+
: typeof input === 'number'
|
|
641
|
+
? new Date(input)
|
|
642
|
+
: parseISO(String(input));
|
|
643
|
+
let result;
|
|
644
|
+
switch (unit) {
|
|
645
|
+
case 'year':
|
|
646
|
+
result = startOfYear(date);
|
|
647
|
+
break;
|
|
648
|
+
case 'quarter':
|
|
649
|
+
result = startOfQuarter(date);
|
|
650
|
+
break;
|
|
651
|
+
case 'month':
|
|
652
|
+
result = startOfMonth(date);
|
|
653
|
+
break;
|
|
654
|
+
case 'week':
|
|
655
|
+
result = startOfWeek(date, { weekStartsOn: 1 });
|
|
656
|
+
break;
|
|
657
|
+
case 'weekSun':
|
|
658
|
+
result = startOfWeek(date, { weekStartsOn: 0 });
|
|
659
|
+
break;
|
|
660
|
+
default:
|
|
661
|
+
return input;
|
|
662
|
+
}
|
|
663
|
+
return result.toJSON();
|
|
664
|
+
},
|
|
665
|
+
'date.endOf'(unit, input) {
|
|
666
|
+
const date = input instanceof Date
|
|
667
|
+
? input
|
|
668
|
+
: typeof input === 'number'
|
|
669
|
+
? new Date(input)
|
|
670
|
+
: parseISO(String(input));
|
|
671
|
+
let result;
|
|
672
|
+
switch (unit) {
|
|
673
|
+
case 'year':
|
|
674
|
+
result = endOfYear(date);
|
|
675
|
+
break;
|
|
676
|
+
case 'quarter':
|
|
677
|
+
result = endOfQuarter(date);
|
|
678
|
+
break;
|
|
679
|
+
case 'month':
|
|
680
|
+
result = endOfMonth(date);
|
|
681
|
+
break;
|
|
682
|
+
case 'week':
|
|
683
|
+
result = endOfWeek(date, { weekStartsOn: 1 });
|
|
684
|
+
break;
|
|
685
|
+
case 'weekSun':
|
|
686
|
+
result = endOfWeek(date, { weekStartsOn: 0 });
|
|
687
|
+
break;
|
|
688
|
+
default:
|
|
689
|
+
return input;
|
|
690
|
+
}
|
|
691
|
+
return result.toJSON();
|
|
692
|
+
},
|
|
693
|
+
'date.set'(args, input, context) {
|
|
694
|
+
const date = input instanceof Date
|
|
695
|
+
? input
|
|
696
|
+
: typeof input === 'number'
|
|
697
|
+
? new Date(input)
|
|
698
|
+
: parseISO(String(input));
|
|
699
|
+
const remapped = mapValues(args, (mapper) => remap(mapper, input, context));
|
|
700
|
+
const setValues = {};
|
|
701
|
+
if (typeof remapped.year === 'number') {
|
|
702
|
+
setValues.year = remapped.year;
|
|
703
|
+
}
|
|
704
|
+
if (typeof remapped.month === 'number') {
|
|
705
|
+
// Convert from 1-indexed (user-facing) to 0-indexed (date-fns)
|
|
706
|
+
setValues.month = remapped.month - 1;
|
|
707
|
+
}
|
|
708
|
+
if (typeof remapped.day === 'number') {
|
|
709
|
+
setValues.date = remapped.day;
|
|
710
|
+
}
|
|
711
|
+
// Convert to UTC-zoned date so set operates in UTC
|
|
712
|
+
const zonedDate = utcToZonedTime(date, 'UTC');
|
|
713
|
+
const result = setDate(zonedDate, setValues);
|
|
714
|
+
return zonedTimeToUtc(result, 'UTC').toJSON();
|
|
715
|
+
},
|
|
497
716
|
'null.strip': (args, input) => stripNullValues(input, args || {}),
|
|
498
717
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
499
718
|
// @ts-ignore 2322 ... is not assignable to type (strictNullChecks)
|
package/types/member.d.ts
CHANGED
|
@@ -70,4 +70,8 @@ export interface AppMemberInfo {
|
|
|
70
70
|
* Unverified email of the user, this will replace the current email when verified.
|
|
71
71
|
*/
|
|
72
72
|
unverifiedEmail?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Whether TOTP (two-factor authentication) is enabled for this app member.
|
|
75
|
+
*/
|
|
76
|
+
totpEnabled?: boolean;
|
|
73
77
|
}
|
package/types/remapper.d.ts
CHANGED
|
@@ -141,6 +141,40 @@ export interface Remappers {
|
|
|
141
141
|
* Please refer to https://date-fns.org/docs/format for the supported patterns.
|
|
142
142
|
*/
|
|
143
143
|
'date.format'?: string;
|
|
144
|
+
/**
|
|
145
|
+
* Takes a date and returns the start of the specified unit.
|
|
146
|
+
*
|
|
147
|
+
* Supported units:
|
|
148
|
+
* - `year`: First day of the year
|
|
149
|
+
* - `quarter`: First day of the quarter
|
|
150
|
+
* - `month`: First day of the month
|
|
151
|
+
* - `week`: First day of the week (Monday)
|
|
152
|
+
* - `weekSun`: First day of the week (Sunday)
|
|
153
|
+
*/
|
|
154
|
+
'date.startOf': 'month' | 'quarter' | 'week' | 'weekSun' | 'year';
|
|
155
|
+
/**
|
|
156
|
+
* Takes a date and returns the end of the specified unit.
|
|
157
|
+
*
|
|
158
|
+
* Supported units:
|
|
159
|
+
* - `year`: Last moment of the year
|
|
160
|
+
* - `quarter`: Last moment of the quarter
|
|
161
|
+
* - `month`: Last moment of the month
|
|
162
|
+
* - `week`: Last moment of the week (ends Sunday if week starts Monday)
|
|
163
|
+
* - `weekSun`: Last moment of the week (ends Saturday if week starts Sunday)
|
|
164
|
+
*/
|
|
165
|
+
'date.endOf': 'month' | 'quarter' | 'week' | 'weekSun' | 'year';
|
|
166
|
+
/**
|
|
167
|
+
* Sets parts of a date.
|
|
168
|
+
*
|
|
169
|
+
* Only the given parts are changed.
|
|
170
|
+
*
|
|
171
|
+
* Month is 1-indexed (1 = January, 12 = December).
|
|
172
|
+
*/
|
|
173
|
+
'date.set': {
|
|
174
|
+
year?: Remapper;
|
|
175
|
+
month?: Remapper;
|
|
176
|
+
day?: Remapper;
|
|
177
|
+
};
|
|
144
178
|
/**
|
|
145
179
|
* Compare all computed remapper values against each other.
|
|
146
180
|
*
|
|
@@ -181,12 +215,24 @@ export interface Remappers {
|
|
|
181
215
|
* Returns `true` of the first entry is greater than the second entry.
|
|
182
216
|
*/
|
|
183
217
|
gt: [Remapper, Remapper];
|
|
218
|
+
/**
|
|
219
|
+
* Compares the first computed remapper value with the second computed remapper value.
|
|
220
|
+
*
|
|
221
|
+
* Returns `true` of the first entry is greater than or equal to the second entry.
|
|
222
|
+
*/
|
|
223
|
+
gte: [Remapper, Remapper];
|
|
184
224
|
/**
|
|
185
225
|
* Compares the first computed remapper value with the second computed remapper value.
|
|
186
226
|
*
|
|
187
227
|
* Returns `true` of the first entry is less than the second entry.
|
|
188
228
|
*/
|
|
189
229
|
lt: [Remapper, Remapper];
|
|
230
|
+
/**
|
|
231
|
+
* Compares the first computed remapper value with the second computed remapper value.
|
|
232
|
+
*
|
|
233
|
+
* Returns `true` of the first entry is less than or equal to the second entry.
|
|
234
|
+
*/
|
|
235
|
+
lte: [Remapper, Remapper];
|
|
190
236
|
/**
|
|
191
237
|
* Logs its input data (returns it) and its context.
|
|
192
238
|
*
|
|
@@ -205,6 +251,13 @@ export interface Remappers {
|
|
|
205
251
|
* Always returns an array, can be empty if supplied data isn’t an array.
|
|
206
252
|
*/
|
|
207
253
|
'array.map': Remapper;
|
|
254
|
+
/**
|
|
255
|
+
* Creates an array of numbers from 0 to N-1.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* { "array.range": 5 } // returns [0, 1, 2, 3, 4]
|
|
259
|
+
*/
|
|
260
|
+
'array.range': Remapper;
|
|
208
261
|
/**
|
|
209
262
|
* Filters out unique entries from an array.
|
|
210
263
|
*
|
|
@@ -438,6 +491,97 @@ export interface Remappers {
|
|
|
438
491
|
*
|
|
439
492
|
*/
|
|
440
493
|
'array.join': string | null;
|
|
494
|
+
/**
|
|
495
|
+
* Groups an array of objects by a common property value.
|
|
496
|
+
*
|
|
497
|
+
* Returns an array of group objects, each containing a `key` (the grouped property value)
|
|
498
|
+
* and `items` (array of objects with that property value).
|
|
499
|
+
*
|
|
500
|
+
* If the input is not an array, returns an empty array.
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* // Input: [{ name: "Alice", dept: "Eng" }, { name: "Bob", dept: "Sales" }, { name: "Charlie", dept: "Eng" }]
|
|
504
|
+
* { "array.groupBy": "dept" }
|
|
505
|
+
* // Result: [
|
|
506
|
+
* // { key: "Eng", items: [{ name: "Alice", dept: "Eng" }, { name: "Charlie", dept: "Eng" }] },
|
|
507
|
+
* // { key: "Sales", items: [{ name: "Bob", dept: "Sales" }] }
|
|
508
|
+
* // ]
|
|
509
|
+
*/
|
|
510
|
+
'array.groupBy': string;
|
|
511
|
+
/**
|
|
512
|
+
* Converts an array of objects into a single object using specified key and value remappers.
|
|
513
|
+
*
|
|
514
|
+
* For each item in the array, the `key` remapper determines the property name and the `value`
|
|
515
|
+
* remapper determines the property value in the resulting object.
|
|
516
|
+
*
|
|
517
|
+
* If the input is not an array, returns an empty object.
|
|
518
|
+
* If multiple items produce the same key, later items overwrite earlier ones.
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* // Input: [{ key: "Eng", items: [...] }, { key: "Sales", items: [...] }]
|
|
522
|
+
* { "array.toObject": { key: { prop: "key" }, value: { prop: "items" } } }
|
|
523
|
+
* // Result: { "Eng": [...], "Sales": [...] }
|
|
524
|
+
*/
|
|
525
|
+
'array.toObject': {
|
|
526
|
+
key: Remapper;
|
|
527
|
+
value: Remapper;
|
|
528
|
+
};
|
|
529
|
+
/**
|
|
530
|
+
* Sorts an array of items.
|
|
531
|
+
*
|
|
532
|
+
* Can sort by a property (for arrays of objects) or by the items themselves (for primitive arrays).
|
|
533
|
+
* Supports ascending and descending order, and handles numbers, strings, and dates.
|
|
534
|
+
*
|
|
535
|
+
* When a string is provided, it's used as the property name to sort by (ascending).
|
|
536
|
+
*
|
|
537
|
+
* **Sorting strategy (`strategy` option):**
|
|
538
|
+
*
|
|
539
|
+
* - `infer` (default): Determines comparison type from the first non-null value.
|
|
540
|
+
* Numbers use numeric comparison, Dates use timestamp comparison, everything else
|
|
541
|
+
* uses lexicographic (string) comparison. Mixed types fall back to lexicographic.
|
|
542
|
+
* - `numeric`: Coerces values to numbers. Non-numeric values (NaN) are pushed to the end.
|
|
543
|
+
* - `lexicographic`: Converts values to strings and uses `localeCompare()`.
|
|
544
|
+
* - `date`: Parses values as ISO dates and compares timestamps. Invalid dates are pushed to the end.
|
|
545
|
+
*
|
|
546
|
+
* Nullish values (null/undefined) are always pushed to the end regardless of strategy.
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* // Input: [3, 1, 2]
|
|
550
|
+
* { "array.sort": null }
|
|
551
|
+
* // Result: [1, 2, 3]
|
|
552
|
+
* @example
|
|
553
|
+
* // Input: [{ name: "Charlie" }, { name: "Alice" }, { name: "Bob" }]
|
|
554
|
+
* { "array.sort": "name" }
|
|
555
|
+
* // Result: [{ name: "Alice" }, { name: "Bob" }, { name: "Charlie" }]
|
|
556
|
+
* @example
|
|
557
|
+
* // Input: [{ name: "Charlie" }, { name: "Alice" }, { name: "Bob" }]
|
|
558
|
+
* { "array.sort": { by: "name", descending: true } }
|
|
559
|
+
* // Result: [{ name: "Charlie" }, { name: "Bob" }, { name: "Alice" }]
|
|
560
|
+
* @example
|
|
561
|
+
* // Input: ["10", "2", "1"]
|
|
562
|
+
* { "array.sort": { strategy: "numeric" } }
|
|
563
|
+
* // Result: ["1", "2", "10"]
|
|
564
|
+
*/
|
|
565
|
+
'array.sort': string | {
|
|
566
|
+
/**
|
|
567
|
+
* The property name to sort by. If not provided, sorts by the items themselves.
|
|
568
|
+
*/
|
|
569
|
+
by?: string;
|
|
570
|
+
/**
|
|
571
|
+
* Whether to sort in descending order. Defaults to false (ascending).
|
|
572
|
+
*/
|
|
573
|
+
descending?: boolean;
|
|
574
|
+
/**
|
|
575
|
+
* The comparison strategy to use.
|
|
576
|
+
*
|
|
577
|
+
* - `infer` (default): Auto-detect from first non-null value. Numbers use numeric,
|
|
578
|
+
* Dates use date, others use lexicographic. Mixed types fall back to lexicographic.
|
|
579
|
+
* - `numeric`: Coerce to numbers. NaN values pushed to end.
|
|
580
|
+
* - `lexicographic`: Convert to strings, use localeCompare().
|
|
581
|
+
* - `date`: Parse as ISO dates, compare timestamps. Invalid dates pushed to end.
|
|
582
|
+
*/
|
|
583
|
+
strategy?: 'date' | 'infer' | 'lexicographic' | 'numeric';
|
|
584
|
+
} | null;
|
|
441
585
|
/**
|
|
442
586
|
* This remapper return true if the provided string is a substring of the input string.
|
|
443
587
|
*
|
|
@@ -593,6 +737,59 @@ export interface Remappers {
|
|
|
593
737
|
b: Remapper;
|
|
594
738
|
operation: 'add' | 'divide' | 'mod' | 'multiply' | 'subtract';
|
|
595
739
|
};
|
|
740
|
+
/**
|
|
741
|
+
* Executes a remapper chain with a temporary root context.
|
|
742
|
+
*
|
|
743
|
+
* This is useful for solving nested context problems, like performing a
|
|
744
|
+
* filter on one list that depends on a value from an outer loop.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
*
|
|
748
|
+
* // Get all customers, and for each customer, find the orders belonging to that customer.
|
|
749
|
+
* // Assumes that `history: 1` is an array of all customers,
|
|
750
|
+
* // and `history: 2` is an array of all orders.
|
|
751
|
+
* {
|
|
752
|
+
* 'array.map': {
|
|
753
|
+
* focus: {
|
|
754
|
+
* on: {
|
|
755
|
+
* 'object.from': {
|
|
756
|
+
* currentCustomer: { array: 'item' },
|
|
757
|
+
* allOrders: { history: 1 },
|
|
758
|
+
* },
|
|
759
|
+
* },
|
|
760
|
+
* do: {
|
|
761
|
+
* 'object.from': {
|
|
762
|
+
* id: { prop: 'id' },
|
|
763
|
+
* name: { prop: 'name' },
|
|
764
|
+
* associatedOrders: [
|
|
765
|
+
* { root: null },
|
|
766
|
+
* { prop: 'allOrders' },
|
|
767
|
+
* {
|
|
768
|
+
* 'array.filter': {
|
|
769
|
+
* equals: [
|
|
770
|
+
* { prop: 'customerId' },
|
|
771
|
+
* [{ root: null }, { prop: 'currentCustomer' }, { prop: 'id' }],
|
|
772
|
+
* ],
|
|
773
|
+
* },
|
|
774
|
+
* },
|
|
775
|
+
* ],
|
|
776
|
+
* },
|
|
777
|
+
* },
|
|
778
|
+
* },
|
|
779
|
+
* },
|
|
780
|
+
* },
|
|
781
|
+
*/
|
|
782
|
+
focus: {
|
|
783
|
+
/**
|
|
784
|
+
* A remapper that resolves to the object that will become the new `root`
|
|
785
|
+
* for the `do` remapper chain.
|
|
786
|
+
*/
|
|
787
|
+
on: Remapper;
|
|
788
|
+
/**
|
|
789
|
+
* The remapper or chain of remappers to execute with the new focused `root`.
|
|
790
|
+
*/
|
|
791
|
+
do: Remapper;
|
|
792
|
+
};
|
|
596
793
|
}
|
|
597
794
|
export type ObjectRemapper = RequireExactlyOne<Remappers>;
|
|
598
795
|
export type ArrayRemapper = (ArrayRemapper | ObjectRemapper)[];
|