@better-giving/endowment 2.0.6 → 3.0.2
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 +7 -2
- 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 +57 -89
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,25 +47,7 @@ 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
|
|
58
|
-
|
|
59
|
-
export const segmentMaxChars = 30;
|
|
60
|
-
export const segment = v.pipe(
|
|
61
|
-
$,
|
|
62
|
-
v.maxLength(
|
|
63
|
-
segmentMaxChars,
|
|
64
|
-
({ requirement: r }) => `cannot exceed ${r} chars`
|
|
65
|
-
),
|
|
66
|
-
//must not be id-like
|
|
67
|
-
v.regex(/^(?!^\d+$)/, "should not be an id"),
|
|
68
|
-
//valid characters
|
|
69
|
-
v.regex(/^[a-zA-Z0-9-._~]+$/, "allowed: numbers | letters | - | . | _ | ~"),
|
|
70
|
-
v.excludes("..", "should not contain double periods"),
|
|
71
|
-
v.custom((x) => !(x as string).startsWith("."), "should not start with dot"),
|
|
72
|
-
v.custom((x) => !(x as string).endsWith("."), "should not end with dot")
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
export const slug = v.lazy((x) => (x ? segment : $));
|
|
50
|
+
export interface IAllocation extends v.InferOutput<typeof allocation> {}
|
|
76
51
|
|
|
77
52
|
export const reg_number = v.pipe(
|
|
78
53
|
$,
|
|
@@ -91,37 +66,37 @@ export const social_media_urls = v.object({
|
|
|
91
66
|
tiktok: v.optional(https_url(false)),
|
|
92
67
|
});
|
|
93
68
|
|
|
94
|
-
export interface
|
|
69
|
+
export interface ISocialMediaURLs
|
|
95
70
|
extends v.InferOutput<typeof social_media_urls> {}
|
|
96
71
|
|
|
97
72
|
export const MAX_RECEIPT_MSG_CHAR = 500;
|
|
98
73
|
|
|
99
|
-
export const
|
|
74
|
+
export const increment_val = v.pipe(
|
|
100
75
|
$req_num_gt0,
|
|
101
76
|
//parsed output
|
|
102
77
|
v.transform((x) => x.toString())
|
|
103
78
|
);
|
|
104
79
|
|
|
105
80
|
export const MAX_NUM_INCREMENTS = 4;
|
|
106
|
-
export const
|
|
107
|
-
export const
|
|
81
|
+
export const increment_label_max_chars = 60;
|
|
82
|
+
export const tagline_max_chars = 140;
|
|
108
83
|
|
|
109
|
-
export const
|
|
84
|
+
export const increment_label = v.pipe(
|
|
110
85
|
$,
|
|
111
86
|
v.maxLength(
|
|
112
|
-
|
|
87
|
+
increment_label_max_chars,
|
|
113
88
|
({ requirement: r }) => `cannot exceed ${r} characters`
|
|
114
89
|
)
|
|
115
90
|
);
|
|
116
91
|
|
|
117
92
|
export const increment = v.object({
|
|
118
|
-
value:
|
|
119
|
-
label:
|
|
93
|
+
value: increment_val,
|
|
94
|
+
label: increment_label,
|
|
120
95
|
});
|
|
121
96
|
|
|
122
|
-
export interface
|
|
97
|
+
export interface IIncrement extends v.InferOutput<typeof increment> {}
|
|
123
98
|
|
|
124
|
-
export const
|
|
99
|
+
export const npo = v.object({
|
|
125
100
|
id: int_gte1,
|
|
126
101
|
env,
|
|
127
102
|
slug: v.optional(slug),
|
|
@@ -129,7 +104,7 @@ export const endowment = v.object({
|
|
|
129
104
|
name: $req,
|
|
130
105
|
endow_designation: org_designation,
|
|
131
106
|
overview: v.optional($),
|
|
132
|
-
tagline: v.optional(v.pipe($, v.maxLength(
|
|
107
|
+
tagline: v.optional(v.pipe($, v.maxLength(tagline_max_chars))),
|
|
133
108
|
image: v.optional(_url),
|
|
134
109
|
logo: v.optional(_url),
|
|
135
110
|
card_img: v.optional(_url),
|
|
@@ -177,93 +152,88 @@ export const endowment = v.object({
|
|
|
177
152
|
payout_minimum: v.optional(v.pipe(v.number(), v.minValue(min_payout_amount))),
|
|
178
153
|
});
|
|
179
154
|
|
|
180
|
-
export const
|
|
181
|
-
v.omit(
|
|
182
|
-
"id",
|
|
183
|
-
"claimed",
|
|
184
|
-
"kyc_donors_only",
|
|
185
|
-
"env",
|
|
186
|
-
"fiscal_sponsored",
|
|
187
|
-
])
|
|
155
|
+
export const npo_update = v.partial(
|
|
156
|
+
v.omit(npo, ["id", "claimed", "kyc_donors_only", "env", "fiscal_sponsored"])
|
|
188
157
|
);
|
|
189
158
|
|
|
190
|
-
export const
|
|
191
|
-
export interface
|
|
192
|
-
export interface
|
|
193
|
-
export type
|
|
159
|
+
export const npo_fields = v.keyof(npo);
|
|
160
|
+
export interface INpo extends v.InferOutput<typeof npo> {}
|
|
161
|
+
export interface INpoUpdate extends v.InferOutput<typeof npo_update> {}
|
|
162
|
+
export type INpoFields = v.InferOutput<typeof npo_fields>;
|
|
194
163
|
|
|
195
164
|
/** for ein path, only fields in reg-num/env gsi is available */
|
|
196
|
-
export const
|
|
197
|
-
fields: v.optional(v.pipe(csvStrs, v.array(
|
|
165
|
+
export const npo_search = v.object({
|
|
166
|
+
fields: v.optional(v.pipe(csvStrs, v.array(npo_fields))),
|
|
198
167
|
});
|
|
199
168
|
|
|
200
|
-
export interface
|
|
201
|
-
extends v.InferInput<typeof endowQueryParams> {}
|
|
169
|
+
export interface INposSearch extends v.InferInput<typeof npo_search> {}
|
|
202
170
|
|
|
203
171
|
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>;
|
|
172
|
+
export const program_id = v.pipe($, v.uuid());
|
|
173
|
+
export const milestone_id = v.pipe($, v.uuid());
|
|
208
174
|
|
|
209
|
-
export const
|
|
175
|
+
export const milestone_new = v.object({
|
|
210
176
|
date: v.pipe($, v.isoTimestamp()),
|
|
211
177
|
title: $,
|
|
212
178
|
description: $,
|
|
213
179
|
media: v.optional(_url),
|
|
214
180
|
});
|
|
215
181
|
|
|
216
|
-
export const
|
|
217
|
-
export interface
|
|
218
|
-
extends v.InferOutput<typeof
|
|
182
|
+
export const milestone_update = v.partial(milestone_new, ["date"]);
|
|
183
|
+
export interface IMilestoneUpdate
|
|
184
|
+
extends v.InferOutput<typeof milestone_update> {}
|
|
219
185
|
|
|
220
|
-
export interface
|
|
221
|
-
export const milestone = v.object({
|
|
222
|
-
|
|
186
|
+
export interface IMilestoneNew extends v.InferOutput<typeof milestone_new> {}
|
|
187
|
+
export const milestone = v.object({
|
|
188
|
+
...milestone_new.entries,
|
|
189
|
+
id: milestone_id,
|
|
190
|
+
});
|
|
191
|
+
export interface IMilestone extends v.InferOutput<typeof milestone> {}
|
|
223
192
|
|
|
224
|
-
export const
|
|
193
|
+
export const program_new = v.object({
|
|
225
194
|
title: $,
|
|
226
195
|
description: $,
|
|
227
196
|
banner: v.optional(_url),
|
|
228
197
|
/** null unsets target */
|
|
229
198
|
targetRaise: v.nullish(amnt),
|
|
230
|
-
milestones: v.pipe(v.array(
|
|
199
|
+
milestones: v.pipe(v.array(milestone_new), v.maxLength(24)),
|
|
231
200
|
});
|
|
232
201
|
|
|
233
202
|
export const program = v.object({
|
|
234
|
-
...v.omit(
|
|
203
|
+
...v.omit(program_new, ["milestones"]).entries,
|
|
235
204
|
/** in USD */
|
|
236
205
|
totalDonations: v.optional(v.number()),
|
|
237
|
-
id:
|
|
206
|
+
id: program_id,
|
|
238
207
|
});
|
|
239
208
|
|
|
240
|
-
export const
|
|
209
|
+
export const program_update = v.partial(v.omit(program_new, ["milestones"]));
|
|
241
210
|
|
|
242
|
-
export interface
|
|
243
|
-
export interface
|
|
244
|
-
export interface
|
|
211
|
+
export interface IProgramNew extends v.InferOutput<typeof program_new> {}
|
|
212
|
+
export interface IProgramDb extends v.InferOutput<typeof program> {}
|
|
213
|
+
export interface IProgram extends v.InferOutput<typeof program> {
|
|
214
|
+
milestones: IMilestone[];
|
|
215
|
+
}
|
|
216
|
+
export interface IProgramUpdate extends v.InferOutput<typeof program_update> {}
|
|
245
217
|
|
|
246
|
-
export const
|
|
218
|
+
export const media_url = v.pipe($, v.url());
|
|
247
219
|
/**
|
|
248
220
|
* so that media is automatically sorted by date
|
|
249
221
|
* @see https://github.com/segmentio/ksuid
|
|
250
222
|
* */
|
|
251
|
-
export const
|
|
252
|
-
export const
|
|
253
|
-
export const
|
|
254
|
-
export type
|
|
223
|
+
export const media_ksuid = $req; // base62;
|
|
224
|
+
export const media_types = ["album", "article", "video"] as const;
|
|
225
|
+
export const media_type = v.picklist(media_types);
|
|
226
|
+
export type TMediaType = v.InferOutput<typeof media_type>;
|
|
255
227
|
|
|
256
|
-
export const
|
|
257
|
-
url: v.optional(
|
|
228
|
+
export const media_update = v.object({
|
|
229
|
+
url: v.optional(media_url),
|
|
258
230
|
featured: v.optional(v.boolean()),
|
|
259
231
|
});
|
|
260
|
-
export interface
|
|
261
|
-
|
|
262
|
-
export type MediaId = v.InferOutput<typeof mediaKsuid>;
|
|
232
|
+
export interface IMediaUpdate extends v.InferOutput<typeof media_update> {}
|
|
263
233
|
|
|
264
|
-
export const
|
|
265
|
-
type: v.optional(
|
|
266
|
-
|
|
234
|
+
export const media_search = v.object({
|
|
235
|
+
type: v.optional(media_type),
|
|
236
|
+
next: v.optional(v.pipe($, v.base64())),
|
|
267
237
|
featured: v.optional(
|
|
268
238
|
v.pipe(
|
|
269
239
|
$,
|
|
@@ -279,7 +249,5 @@ export const mediaQueryParams = v.object({
|
|
|
279
249
|
)
|
|
280
250
|
),
|
|
281
251
|
});
|
|
282
|
-
export interface
|
|
283
|
-
|
|
284
|
-
export interface MediaQueryParamsObj
|
|
285
|
-
extends v.InferOutput<typeof mediaQueryParams> {}
|
|
252
|
+
export interface IMediaSearch extends v.InferInput<typeof media_search> {}
|
|
253
|
+
export interface IMediaSearchObj extends v.InferOutput<typeof media_search> {}
|