@better-giving/endowment 2.0.6 → 3.0.0
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/cloudsearch.d.mts +1 -1
- package/dist/cloudsearch.mjs +3 -3
- package/dist/db.d.mts +138 -115
- package/dist/db.mjs +355 -4
- package/dist/index.d.mts +2 -20
- package/dist/index.mjs +2 -1
- package/dist/interfaces.d.mts +41 -0
- package/dist/interfaces.mjs +1 -0
- package/dist/media.d.mts +11 -0
- package/dist/media.mjs +48 -0
- package/dist/npo.d.mts +4 -0
- package/dist/npo.mjs +41 -0
- package/dist/schema.d.mts +42 -43
- package/dist/schema.mjs +36 -40
- package/package.json +6 -1
- package/src/cloudsearch.mts +3 -2
- package/src/db.mts +414 -124
- package/src/index.mts +2 -45
- package/src/interfaces.mts +73 -0
- package/src/media.mts +67 -0
- package/src/npo.mts +47 -0
- package/src/schema.mts +59 -73
package/src/media.mts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { TRecord } from "@better-giving/db";
|
|
2
|
+
import type { IMedia, TBinFlag } from "./interfaces.mjs";
|
|
3
|
+
import type { IMediaSearchObj, TMediaType } from "./schema.mjs";
|
|
4
|
+
|
|
5
|
+
export const med_sk = (ksuid: string, type: TMediaType, featured: TBinFlag) =>
|
|
6
|
+
`MediaList#${featured}#${ksuid}#${type}`;
|
|
7
|
+
|
|
8
|
+
export const med_sk_attr = (sk: string) => {
|
|
9
|
+
const [featured, ksuid, type] = sk.split("#").slice(1) as [
|
|
10
|
+
TBinFlag,
|
|
11
|
+
string,
|
|
12
|
+
TMediaType,
|
|
13
|
+
];
|
|
14
|
+
return { featured, ksuid, type };
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function med_key_filter(
|
|
18
|
+
PK: string,
|
|
19
|
+
params: IMediaSearchObj
|
|
20
|
+
): [string, Record<string, string>] {
|
|
21
|
+
/** KSUID string is base62, length:27 */
|
|
22
|
+
const min_ksuid = "0".repeat(27);
|
|
23
|
+
const max_ksuid = "z".repeat(27);
|
|
24
|
+
|
|
25
|
+
//media categories: get all media per type, all featured - sorted by date
|
|
26
|
+
if (params.featured && params.type) {
|
|
27
|
+
return [
|
|
28
|
+
`gsi1PK = :pk AND gsi1SK BETWEEN :startSK and :endSK`,
|
|
29
|
+
{
|
|
30
|
+
":pk": PK,
|
|
31
|
+
":startSK": med_sk(min_ksuid, params.type, "0"),
|
|
32
|
+
":endSK": med_sk(max_ksuid, params.type, "0"),
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//media type page: get all media per type - sorted by featured and date
|
|
38
|
+
if (params.type) {
|
|
39
|
+
return [
|
|
40
|
+
`gsi1PK = :pk AND gsi1SK BETWEEN :startSK and :endSK`,
|
|
41
|
+
{
|
|
42
|
+
":pk": PK,
|
|
43
|
+
":startSK": med_sk(min_ksuid, params.type, "0"),
|
|
44
|
+
":endSK": med_sk(max_ksuid, params.type, "1"),
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//endow-profile: get all media, all featured - sorted by date
|
|
50
|
+
if (params.featured) {
|
|
51
|
+
return [
|
|
52
|
+
`gsi1PK = :pk AND begins_with(gsi1SK, :sk)`,
|
|
53
|
+
{ ":pk": PK, ":sk": `MediaList#0` },
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return [
|
|
58
|
+
`gsi1PK = :pk AND begins_with(gsi1SK, :sk)`,
|
|
59
|
+
{ ":pk": PK, ":sk": `MediaList#` },
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const to_imedia = (d: TRecord): IMedia => {
|
|
64
|
+
const { gsi1SK, gsi1PK, PK, SK, ...rest } = d;
|
|
65
|
+
const x = med_sk_attr(gsi1SK);
|
|
66
|
+
return { ...rest, featured: x.featured === "1" } as any;
|
|
67
|
+
};
|
package/src/npo.mts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { INpo } from "./schema.mjs";
|
|
2
|
+
|
|
3
|
+
const npo_fields: { [K in keyof Required<INpo>]: "" } = {
|
|
4
|
+
id: "",
|
|
5
|
+
active_in_countries: "",
|
|
6
|
+
endow_designation: "",
|
|
7
|
+
fiscal_sponsored: "",
|
|
8
|
+
hide_bg_tip: "",
|
|
9
|
+
hq_country: "",
|
|
10
|
+
image: "",
|
|
11
|
+
kyc_donors_only: "",
|
|
12
|
+
logo: "",
|
|
13
|
+
name: "",
|
|
14
|
+
overview: "",
|
|
15
|
+
published: "",
|
|
16
|
+
registration_number: "",
|
|
17
|
+
social_media_urls: "",
|
|
18
|
+
sdgs: "",
|
|
19
|
+
street_address: "",
|
|
20
|
+
tagline: "",
|
|
21
|
+
url: "",
|
|
22
|
+
slug: "",
|
|
23
|
+
claimed: "",
|
|
24
|
+
card_img: "",
|
|
25
|
+
progDonationsAllowed: "",
|
|
26
|
+
allocation: "",
|
|
27
|
+
donateMethods: "",
|
|
28
|
+
increments: "",
|
|
29
|
+
receiptMsg: "",
|
|
30
|
+
fund_opt_in: "",
|
|
31
|
+
env: "",
|
|
32
|
+
target: "",
|
|
33
|
+
referrer: "",
|
|
34
|
+
referrer_expiry: "",
|
|
35
|
+
referral_id: "",
|
|
36
|
+
w_form: "",
|
|
37
|
+
payout_minimum: "",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function projection(fields: string[] = Object.keys(npo_fields)) {
|
|
41
|
+
const expression = fields.map((n) => `#${n}`).join(",");
|
|
42
|
+
const names = fields.reduce(
|
|
43
|
+
(prev, curr) => ({ ...prev, [`#${curr}`]: curr }),
|
|
44
|
+
{} as Record<string, string>
|
|
45
|
+
);
|
|
46
|
+
return { expression, names };
|
|
47
|
+
}
|
package/src/schema.mts
CHANGED
|
@@ -11,13 +11,6 @@ import {
|
|
|
11
11
|
org_designation,
|
|
12
12
|
unsdg_num,
|
|
13
13
|
} from "@better-giving/schemas";
|
|
14
|
-
export {
|
|
15
|
-
type OrgDesignation as EndowDesignation,
|
|
16
|
-
type Environment,
|
|
17
|
-
type UnSdgNum,
|
|
18
|
-
type DonateMethodId,
|
|
19
|
-
https_url,
|
|
20
|
-
} from "@better-giving/schemas";
|
|
21
14
|
export {
|
|
22
15
|
org_designation as endow_designation,
|
|
23
16
|
env,
|
|
@@ -54,13 +47,13 @@ export const allocation = v.pipe(
|
|
|
54
47
|
v.check((x) => x.cash + x.liq + x.lock === 100, "must total to 100")
|
|
55
48
|
);
|
|
56
49
|
|
|
57
|
-
export interface
|
|
50
|
+
export interface IAllocation extends v.InferOutput<typeof allocation> {}
|
|
58
51
|
|
|
59
|
-
export const
|
|
52
|
+
export const segment_max_chars = 30;
|
|
60
53
|
export const segment = v.pipe(
|
|
61
54
|
$,
|
|
62
55
|
v.maxLength(
|
|
63
|
-
|
|
56
|
+
segment_max_chars,
|
|
64
57
|
({ requirement: r }) => `cannot exceed ${r} chars`
|
|
65
58
|
),
|
|
66
59
|
//must not be id-like
|
|
@@ -91,37 +84,37 @@ export const social_media_urls = v.object({
|
|
|
91
84
|
tiktok: v.optional(https_url(false)),
|
|
92
85
|
});
|
|
93
86
|
|
|
94
|
-
export interface
|
|
87
|
+
export interface ISocialMediaURLs
|
|
95
88
|
extends v.InferOutput<typeof social_media_urls> {}
|
|
96
89
|
|
|
97
90
|
export const MAX_RECEIPT_MSG_CHAR = 500;
|
|
98
91
|
|
|
99
|
-
export const
|
|
92
|
+
export const increment_val = v.pipe(
|
|
100
93
|
$req_num_gt0,
|
|
101
94
|
//parsed output
|
|
102
95
|
v.transform((x) => x.toString())
|
|
103
96
|
);
|
|
104
97
|
|
|
105
98
|
export const MAX_NUM_INCREMENTS = 4;
|
|
106
|
-
export const
|
|
107
|
-
export const
|
|
99
|
+
export const increment_label_max_chars = 60;
|
|
100
|
+
export const tagline_max_chars = 140;
|
|
108
101
|
|
|
109
|
-
export const
|
|
102
|
+
export const increment_label = v.pipe(
|
|
110
103
|
$,
|
|
111
104
|
v.maxLength(
|
|
112
|
-
|
|
105
|
+
increment_label_max_chars,
|
|
113
106
|
({ requirement: r }) => `cannot exceed ${r} characters`
|
|
114
107
|
)
|
|
115
108
|
);
|
|
116
109
|
|
|
117
110
|
export const increment = v.object({
|
|
118
|
-
value:
|
|
119
|
-
label:
|
|
111
|
+
value: increment_val,
|
|
112
|
+
label: increment_label,
|
|
120
113
|
});
|
|
121
114
|
|
|
122
|
-
export interface
|
|
115
|
+
export interface IIncrement extends v.InferOutput<typeof increment> {}
|
|
123
116
|
|
|
124
|
-
export const
|
|
117
|
+
export const npo = v.object({
|
|
125
118
|
id: int_gte1,
|
|
126
119
|
env,
|
|
127
120
|
slug: v.optional(slug),
|
|
@@ -129,7 +122,7 @@ export const endowment = v.object({
|
|
|
129
122
|
name: $req,
|
|
130
123
|
endow_designation: org_designation,
|
|
131
124
|
overview: v.optional($),
|
|
132
|
-
tagline: v.optional(v.pipe($, v.maxLength(
|
|
125
|
+
tagline: v.optional(v.pipe($, v.maxLength(tagline_max_chars))),
|
|
133
126
|
image: v.optional(_url),
|
|
134
127
|
logo: v.optional(_url),
|
|
135
128
|
card_img: v.optional(_url),
|
|
@@ -177,93 +170,88 @@ export const endowment = v.object({
|
|
|
177
170
|
payout_minimum: v.optional(v.pipe(v.number(), v.minValue(min_payout_amount))),
|
|
178
171
|
});
|
|
179
172
|
|
|
180
|
-
export const
|
|
181
|
-
v.omit(
|
|
182
|
-
"id",
|
|
183
|
-
"claimed",
|
|
184
|
-
"kyc_donors_only",
|
|
185
|
-
"env",
|
|
186
|
-
"fiscal_sponsored",
|
|
187
|
-
])
|
|
173
|
+
export const npo_update = v.partial(
|
|
174
|
+
v.omit(npo, ["id", "claimed", "kyc_donors_only", "env", "fiscal_sponsored"])
|
|
188
175
|
);
|
|
189
176
|
|
|
190
|
-
export const
|
|
191
|
-
export interface
|
|
192
|
-
export interface
|
|
193
|
-
export type
|
|
177
|
+
export const npo_fields = v.keyof(npo);
|
|
178
|
+
export interface INpo extends v.InferOutput<typeof npo> {}
|
|
179
|
+
export interface INpoUpdate extends v.InferOutput<typeof npo_update> {}
|
|
180
|
+
export type INpoFields = v.InferOutput<typeof npo_fields>;
|
|
194
181
|
|
|
195
182
|
/** for ein path, only fields in reg-num/env gsi is available */
|
|
196
|
-
export const
|
|
197
|
-
fields: v.optional(v.pipe(csvStrs, v.array(
|
|
183
|
+
export const npo_search = v.object({
|
|
184
|
+
fields: v.optional(v.pipe(csvStrs, v.array(npo_fields))),
|
|
198
185
|
});
|
|
199
186
|
|
|
200
|
-
export interface
|
|
201
|
-
extends v.InferInput<typeof endowQueryParams> {}
|
|
187
|
+
export interface INposSearch extends v.InferInput<typeof npo_search> {}
|
|
202
188
|
|
|
203
189
|
const amnt = v.pipe(v.number(), v.minValue(0));
|
|
204
|
-
export const
|
|
205
|
-
export
|
|
206
|
-
export const milestoneId = v.pipe($, v.uuid());
|
|
207
|
-
export type MilestoneId = v.InferOutput<typeof milestoneId>;
|
|
190
|
+
export const program_id = v.pipe($, v.uuid());
|
|
191
|
+
export const milestone_id = v.pipe($, v.uuid());
|
|
208
192
|
|
|
209
|
-
export const
|
|
193
|
+
export const milestone_new = v.object({
|
|
210
194
|
date: v.pipe($, v.isoTimestamp()),
|
|
211
195
|
title: $,
|
|
212
196
|
description: $,
|
|
213
197
|
media: v.optional(_url),
|
|
214
198
|
});
|
|
215
199
|
|
|
216
|
-
export const
|
|
217
|
-
export interface
|
|
218
|
-
extends v.InferOutput<typeof
|
|
200
|
+
export const milestone_update = v.partial(milestone_new, ["date"]);
|
|
201
|
+
export interface IMilestoneUpdate
|
|
202
|
+
extends v.InferOutput<typeof milestone_update> {}
|
|
219
203
|
|
|
220
|
-
export interface
|
|
221
|
-
export const milestone = v.object({
|
|
222
|
-
|
|
204
|
+
export interface IMilestoneNew extends v.InferOutput<typeof milestone_new> {}
|
|
205
|
+
export const milestone = v.object({
|
|
206
|
+
...milestone_new.entries,
|
|
207
|
+
id: milestone_id,
|
|
208
|
+
});
|
|
209
|
+
export interface IMilestone extends v.InferOutput<typeof milestone> {}
|
|
223
210
|
|
|
224
|
-
export const
|
|
211
|
+
export const program_new = v.object({
|
|
225
212
|
title: $,
|
|
226
213
|
description: $,
|
|
227
214
|
banner: v.optional(_url),
|
|
228
215
|
/** null unsets target */
|
|
229
216
|
targetRaise: v.nullish(amnt),
|
|
230
|
-
milestones: v.pipe(v.array(
|
|
217
|
+
milestones: v.pipe(v.array(milestone_new), v.maxLength(24)),
|
|
231
218
|
});
|
|
232
219
|
|
|
233
220
|
export const program = v.object({
|
|
234
|
-
...v.omit(
|
|
221
|
+
...v.omit(program_new, ["milestones"]).entries,
|
|
235
222
|
/** in USD */
|
|
236
223
|
totalDonations: v.optional(v.number()),
|
|
237
|
-
id:
|
|
224
|
+
id: program_id,
|
|
238
225
|
});
|
|
239
226
|
|
|
240
|
-
export const
|
|
227
|
+
export const program_update = v.partial(v.omit(program_new, ["milestones"]));
|
|
241
228
|
|
|
242
|
-
export interface
|
|
243
|
-
export interface
|
|
244
|
-
export interface
|
|
229
|
+
export interface IProgramNew extends v.InferOutput<typeof program_new> {}
|
|
230
|
+
export interface IProgramDb extends v.InferOutput<typeof program> {}
|
|
231
|
+
export interface IProgram extends v.InferOutput<typeof program> {
|
|
232
|
+
milestones: IMilestone[];
|
|
233
|
+
}
|
|
234
|
+
export interface IProgramUpdate extends v.InferOutput<typeof program_update> {}
|
|
245
235
|
|
|
246
|
-
export const
|
|
236
|
+
export const media_url = v.pipe($, v.url());
|
|
247
237
|
/**
|
|
248
238
|
* so that media is automatically sorted by date
|
|
249
239
|
* @see https://github.com/segmentio/ksuid
|
|
250
240
|
* */
|
|
251
|
-
export const
|
|
252
|
-
export const
|
|
253
|
-
export const
|
|
254
|
-
export type
|
|
241
|
+
export const media_ksuid = $req; // base62;
|
|
242
|
+
export const media_types = ["album", "article", "video"] as const;
|
|
243
|
+
export const media_type = v.picklist(media_types);
|
|
244
|
+
export type TMediaType = v.InferOutput<typeof media_type>;
|
|
255
245
|
|
|
256
|
-
export const
|
|
257
|
-
url: v.optional(
|
|
246
|
+
export const media_update = v.object({
|
|
247
|
+
url: v.optional(media_url),
|
|
258
248
|
featured: v.optional(v.boolean()),
|
|
259
249
|
});
|
|
260
|
-
export interface
|
|
261
|
-
|
|
262
|
-
export type MediaId = v.InferOutput<typeof mediaKsuid>;
|
|
250
|
+
export interface IMediaUpdate extends v.InferOutput<typeof media_update> {}
|
|
263
251
|
|
|
264
|
-
export const
|
|
265
|
-
type: v.optional(
|
|
266
|
-
|
|
252
|
+
export const media_search = v.object({
|
|
253
|
+
type: v.optional(media_type),
|
|
254
|
+
next: v.optional(v.pipe($, v.base64())),
|
|
267
255
|
featured: v.optional(
|
|
268
256
|
v.pipe(
|
|
269
257
|
$,
|
|
@@ -279,7 +267,5 @@ export const mediaQueryParams = v.object({
|
|
|
279
267
|
)
|
|
280
268
|
),
|
|
281
269
|
});
|
|
282
|
-
export interface
|
|
283
|
-
|
|
284
|
-
export interface MediaQueryParamsObj
|
|
285
|
-
extends v.InferOutput<typeof mediaQueryParams> {}
|
|
270
|
+
export interface IMediaSearch extends v.InferInput<typeof media_search> {}
|
|
271
|
+
export interface IMediaSearchObj extends v.InferOutput<typeof media_search> {}
|