@better-giving/endowment 2.0.5 → 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/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 Allocation extends v.InferOutput<typeof allocation> {}
50
+ export interface IAllocation extends v.InferOutput<typeof allocation> {}
58
51
 
59
- export const segmentMaxChars = 30;
52
+ export const segment_max_chars = 30;
60
53
  export const segment = v.pipe(
61
54
  $,
62
55
  v.maxLength(
63
- segmentMaxChars,
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 SocialMediaURLs
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 incrementVal = v.pipe(
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 incrementLabelMaxChars = 60;
107
- export const taglineMaxChars = 140;
99
+ export const increment_label_max_chars = 60;
100
+ export const tagline_max_chars = 140;
108
101
 
109
- export const incrementLabel = v.pipe(
102
+ export const increment_label = v.pipe(
110
103
  $,
111
104
  v.maxLength(
112
- incrementLabelMaxChars,
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: incrementVal,
119
- label: incrementLabel,
111
+ value: increment_val,
112
+ label: increment_label,
120
113
  });
121
114
 
122
- export interface Increment extends v.InferOutput<typeof increment> {}
115
+ export interface IIncrement extends v.InferOutput<typeof increment> {}
123
116
 
124
- export const endowment = v.object({
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(taglineMaxChars))),
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 endowUpdate = v.partial(
181
- v.omit(endowment, [
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 endowFields = v.keyof(endowment);
191
- export interface Endowment extends v.InferOutput<typeof endowment> {}
192
- export interface EndowUpdate extends v.InferOutput<typeof endowUpdate> {}
193
- export type EndowFields = v.InferOutput<typeof endowFields>;
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 endowQueryParams = v.object({
197
- fields: v.optional(v.pipe(csvStrs, v.array(endowFields))),
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 EndowQueryParams
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 programId = v.pipe($, v.uuid());
205
- export type ProgramId = v.InferOutput<typeof programId>;
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 newMilestone = v.object({
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 milestoneUpdate = v.partial(newMilestone, ["date"]);
217
- export interface MilestoneUpdate
218
- extends v.InferOutput<typeof milestoneUpdate> {}
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 NewMilestone extends v.InferOutput<typeof newMilestone> {}
221
- export const milestone = v.object({ ...newMilestone.entries, id: milestoneId });
222
- export interface Milestone extends v.InferOutput<typeof milestone> {}
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 newProgram = v.object({
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(newMilestone), v.maxLength(24)),
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(newProgram, ["milestones"]).entries,
221
+ ...v.omit(program_new, ["milestones"]).entries,
235
222
  /** in USD */
236
223
  totalDonations: v.optional(v.number()),
237
- id: programId,
224
+ id: program_id,
238
225
  });
239
226
 
240
- export const programUpdate = v.partial(v.omit(newProgram, ["milestones"]));
227
+ export const program_update = v.partial(v.omit(program_new, ["milestones"]));
241
228
 
242
- export interface NewProgram extends v.InferOutput<typeof newProgram> {}
243
- export interface Program extends v.InferOutput<typeof program> {}
244
- export interface ProgramUpdate extends v.InferOutput<typeof programUpdate> {}
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 mediaUrl = v.pipe($, v.url());
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 mediaKsuid = $req; // base62;
252
- export const mediaTypes = ["album", "article", "video"] as const;
253
- export const mediaType = v.picklist(mediaTypes);
254
- export type MediaType = v.InferOutput<typeof mediaType>;
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 mediaUpdate = v.object({
257
- url: v.optional(mediaUrl),
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 MediaUpdate extends v.InferOutput<typeof mediaUpdate> {}
261
-
262
- export type MediaId = v.InferOutput<typeof mediaKsuid>;
250
+ export interface IMediaUpdate extends v.InferOutput<typeof media_update> {}
263
251
 
264
- export const mediaQueryParams = v.object({
265
- type: v.optional(mediaType),
266
- nextPageKey: v.optional(v.pipe($, v.base64())),
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 MediaQueryParams
283
- extends v.InferInput<typeof mediaQueryParams> {}
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> {}