@better-giving/fundraiser 3.0.5 → 3.0.7
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 +3 -2
- package/dist/db.d.mts +37 -19
- package/dist/db.mjs +68 -3
- package/dist/index.d.mts +2 -16
- package/dist/interfaces.d.mts +28 -0
- package/dist/interfaces.mjs +1 -0
- package/dist/schema.d.mts +13 -32
- package/dist/schema.mjs +17 -17
- package/package.json +4 -3
- package/src/cloudsearch.mts +4 -3
- package/src/db.mts +73 -15
- package/src/index.mts +6 -22
- package/src/interfaces.mts +31 -0
- package/src/schema.mts +24 -55
package/dist/cloudsearch.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ToDoc, ToHitFields, ToUpdate } from "@better-giving/types/cloudsearch";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type { IFundInternal } from "./interfaces.mjs";
|
|
3
|
+
import type { IFundNew } from "./schema.mjs";
|
|
4
|
+
export interface CloudsearchFund extends Pick<IFundNew, "name" | "description" | "logo" | "banner" | "featured" | "members" | "target">, Pick<IFundInternal, "id" | "env" | "active" | "verified" | "donation_total_usd" | "creator_id" | "creator_name"> {
|
|
4
5
|
/** iso | "9999-12-31T23:59:59Z" year 9999 */
|
|
5
6
|
expiration: string;
|
|
6
7
|
}
|
package/dist/db.d.mts
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { Db, type TxType } from "@better-giving/db";
|
|
2
|
+
import type { IFund } from "./interfaces.mjs";
|
|
3
|
+
import type { IFundUpdate } from "./schema.mjs";
|
|
4
|
+
export declare class FundDb extends Db {
|
|
5
|
+
static readonly table = "funds";
|
|
6
|
+
static readonly slug_env_gsi = "slug-env-gsi";
|
|
7
|
+
key_fund(id: string): {
|
|
8
|
+
PK: string;
|
|
9
|
+
SK: string;
|
|
10
|
+
};
|
|
11
|
+
fund_record(data: IFund): {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
banner: string;
|
|
15
|
+
logo: string;
|
|
16
|
+
members: number[];
|
|
17
|
+
target: string;
|
|
18
|
+
videos: string[];
|
|
19
|
+
featured: boolean;
|
|
20
|
+
npo_owner: number;
|
|
21
|
+
expiration?: string | undefined;
|
|
22
|
+
slug?: string | undefined;
|
|
23
|
+
id: string;
|
|
24
|
+
env: import("@better-giving/schemas").Environment;
|
|
25
|
+
active: boolean;
|
|
26
|
+
verified: boolean;
|
|
27
|
+
donation_total_usd: number;
|
|
28
|
+
settings: import("./interfaces.mjs").IFundSettings;
|
|
29
|
+
creator_id: string;
|
|
30
|
+
creator_name: string;
|
|
31
|
+
PK: string;
|
|
32
|
+
SK: string;
|
|
33
|
+
};
|
|
34
|
+
fund_put_txi(data: IFund): TxType["Put"];
|
|
35
|
+
/**@param id - slug or uuid */
|
|
36
|
+
fund(id: string): Promise<IFund | undefined>;
|
|
37
|
+
fund_edit(id: string, { target, slug, ...update }: IFundUpdate): Promise<Record<string, any>>;
|
|
20
38
|
}
|
package/dist/db.mjs
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
};
|
|
1
|
+
import { Db, UpdateBuilder } from "@better-giving/db";
|
|
2
|
+
import { QueryCommand, UpdateCommand, } from "@aws-sdk/lib-dynamodb";
|
|
3
|
+
import { UUID_REGEX } from "valibot";
|
|
4
|
+
export class FundDb extends Db {
|
|
5
|
+
static table = "funds";
|
|
6
|
+
static slug_env_gsi = "slug-env-gsi";
|
|
7
|
+
key_fund(id) {
|
|
8
|
+
return {
|
|
9
|
+
PK: `Fund#${id}`,
|
|
10
|
+
SK: `Fund#${id}`,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
fund_record(data) {
|
|
14
|
+
return {
|
|
15
|
+
...this.key_fund(data.id),
|
|
16
|
+
...data,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
fund_put_txi(data) {
|
|
20
|
+
return {
|
|
21
|
+
TableName: FundDb.table,
|
|
22
|
+
Item: this.fund_record(data),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**@param id - slug or uuid */
|
|
26
|
+
async fund(id) {
|
|
27
|
+
const q = {
|
|
28
|
+
TableName: FundDb.table,
|
|
29
|
+
Limit: 1,
|
|
30
|
+
};
|
|
31
|
+
if (UUID_REGEX.test(id)) {
|
|
32
|
+
q.KeyConditionExpression = "PK = :PK AND SK = :SK";
|
|
33
|
+
q.ExpressionAttributeValues = this.key_fund(id);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
q.IndexName = FundDb.slug_env_gsi;
|
|
37
|
+
q.KeyConditionExpression = "slug = :slug AND env = :env";
|
|
38
|
+
q.ExpressionAttributeValues = {
|
|
39
|
+
":slug": id,
|
|
40
|
+
":env": this.env,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const { Items: i = [] } = await this.client.send(new QueryCommand(q));
|
|
44
|
+
return i[0] && this.sans_keys(i[0]);
|
|
45
|
+
}
|
|
46
|
+
async fund_edit(id, { target, slug, ...update }) {
|
|
47
|
+
const updates = new UpdateBuilder();
|
|
48
|
+
if (slug)
|
|
49
|
+
updates.set("slug", slug);
|
|
50
|
+
if (slug === "")
|
|
51
|
+
updates.remove("slug");
|
|
52
|
+
if (target || target === "0") {
|
|
53
|
+
updates.set("target", target);
|
|
54
|
+
}
|
|
55
|
+
for (const [k, v] of Object.entries(update)) {
|
|
56
|
+
if (v === undefined)
|
|
57
|
+
continue;
|
|
58
|
+
updates.set(k, v);
|
|
59
|
+
}
|
|
60
|
+
const command = new UpdateCommand({
|
|
61
|
+
TableName: FundDb.table,
|
|
62
|
+
Key: this.key_fund(id),
|
|
63
|
+
ReturnValues: "ALL_NEW",
|
|
64
|
+
...updates.collect(),
|
|
65
|
+
});
|
|
66
|
+
return this.client.send(command).then((res) => res.Attributes ?? {});
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { FundInternal, NewFund } from "./schema.mjs";
|
|
4
|
-
export type { Environment, FundSettings, FundUpdate, FundsEndowMemberOfParams, FundsParams, NewFund, } from "./schema.mjs";
|
|
5
|
-
export type { FundItem };
|
|
6
|
-
export interface FundMember {
|
|
7
|
-
id: number;
|
|
8
|
-
name: string;
|
|
9
|
-
logo: string | undefined;
|
|
10
|
-
banner: string | undefined;
|
|
11
|
-
}
|
|
12
|
-
export interface SingleFund extends Omit<NewFund, "members">, FundInternal {
|
|
13
|
-
members: FundMember[];
|
|
14
|
-
}
|
|
15
|
-
export interface FundsPage extends IPageNumbered<FundItem> {
|
|
16
|
-
}
|
|
1
|
+
export type { CloudsearchFund as FundItem } from "./cloudsearch.mjs";
|
|
2
|
+
export type { IFundNew, IFundUpdate, IFundsNpoMemberOfSearchObj, IFundsSearchObj, DonateMethodId, Environment, } from "./schema.mjs";
|
|
17
3
|
export declare const MAX_EXPIRATION = "9999-12-31T23:59:59Z";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DonateMethodId, Environment } from "@better-giving/schemas";
|
|
2
|
+
import type { IFundNew } from "./schema.mjs";
|
|
3
|
+
import type { IPageNumbered } from "@better-giving/types/api";
|
|
4
|
+
import type { CloudsearchFund } from "./cloudsearch.mjs";
|
|
5
|
+
export interface IFundSettings {
|
|
6
|
+
hide_bg_tip: boolean;
|
|
7
|
+
donateMethods?: DonateMethodId[];
|
|
8
|
+
}
|
|
9
|
+
export interface IFundCreator {
|
|
10
|
+
/** "{number}" - endow id, "{email} - user*/
|
|
11
|
+
creator_id: string;
|
|
12
|
+
creator_name: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IFundInternal extends IFundCreator {
|
|
15
|
+
/** uuid */
|
|
16
|
+
id: string;
|
|
17
|
+
env: Environment;
|
|
18
|
+
/** fund can be closed before expiration */
|
|
19
|
+
active: boolean;
|
|
20
|
+
verified: boolean;
|
|
21
|
+
/** to date received: initialized to `0` */
|
|
22
|
+
donation_total_usd: number;
|
|
23
|
+
settings: IFundSettings;
|
|
24
|
+
}
|
|
25
|
+
export interface IFund extends IFundNew, IFundInternal {
|
|
26
|
+
}
|
|
27
|
+
export interface IFundsPage extends IPageNumbered<CloudsearchFund> {
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/schema.d.mts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { DonateMethodId } from "@better-giving/schemas";
|
|
2
|
-
import type { Environment } from "@better-giving/types/list";
|
|
3
|
-
export type { Environment } from "@better-giving/types/list";
|
|
4
1
|
import { type InferOutput } from "valibot";
|
|
5
|
-
export
|
|
2
|
+
export { type DonateMethodId, slug } from "@better-giving/schemas";
|
|
3
|
+
export type { Environment } from "@better-giving/types/list";
|
|
4
|
+
export declare const fund_id: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").UuidAction<string, undefined>]>;
|
|
6
5
|
/**
|
|
7
6
|
* when fundraiser is created in the context of an NPO, all members of that NPO can edit the fundraiser
|
|
8
7
|
* 0 - none
|
|
9
8
|
*/
|
|
10
9
|
export declare const npo_owner: import("valibot").SchemaWithPipe<[import("valibot").NumberSchema<undefined>, import("valibot").IntegerAction<number, undefined>, import("valibot").MinValueAction<number, 0, undefined>]>;
|
|
11
|
-
export declare const
|
|
10
|
+
export declare const fund_new: import("valibot").ObjectSchema<{
|
|
12
11
|
readonly name: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
13
12
|
readonly description: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
14
13
|
readonly banner: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").UrlAction<string, undefined>]>;
|
|
@@ -23,7 +22,7 @@ export declare const newFund: import("valibot").ObjectSchema<{
|
|
|
23
22
|
readonly npo_owner: import("valibot").SchemaWithPipe<[import("valibot").NumberSchema<undefined>, import("valibot").IntegerAction<number, undefined>, import("valibot").MinValueAction<number, 0, undefined>]>;
|
|
24
23
|
readonly slug: import("valibot").OptionalSchema<import("valibot").LazySchema<import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]> | import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").MaxLengthAction<string, 30, ({ requirement: r }: import("valibot").MaxLengthIssue<string, 30>) => string>, import("valibot").RegexAction<string, "should not be an id">, import("valibot").RegexAction<string, "allowed: numbers | letters | - | . | _ | ~">, import("valibot").ExcludesAction<string, "..", "should not contain double periods">, import("valibot").CustomSchema<string, "should not start with dot">, import("valibot").CustomSchema<string, "should not end with dot">]>>, never>;
|
|
25
24
|
}, undefined>;
|
|
26
|
-
export declare const
|
|
25
|
+
export declare const fund_update: Omit<Omit<import("valibot").ObjectSchema<{
|
|
27
26
|
readonly name: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
28
27
|
readonly description: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").NonEmptyAction<string, "required">]>;
|
|
29
28
|
readonly banner: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").UrlAction<string, undefined>]>;
|
|
@@ -131,38 +130,20 @@ export declare const fundUpdate: Omit<Omit<import("valibot").ObjectSchema<{
|
|
|
131
130
|
readonly issue: import("valibot").StringIssue | import("valibot").NumberIssue | import("valibot").MinValueIssue<number, 0> | import("valibot").NonEmptyIssue<string> | import("valibot").UrlIssue<string> | import("valibot").ArrayIssue | import("valibot").BooleanIssue | import("valibot").LiteralIssue | import("valibot").UnionIssue<import("valibot").StringIssue | import("valibot").NumberIssue | import("valibot").MinValueIssue<number, 0> | import("valibot").LiteralIssue> | import("valibot").CustomIssue | import("valibot").MaxLengthIssue<string, 30> | import("valibot").RegexIssue<string> | import("valibot").ExcludesIssue<string, ".."> | import("valibot").ObjectIssue;
|
|
132
131
|
} | undefined;
|
|
133
132
|
};
|
|
134
|
-
export declare const
|
|
133
|
+
export declare const funds_search: import("valibot").ObjectSchema<{
|
|
135
134
|
/** search text */
|
|
136
135
|
readonly query: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, never>;
|
|
137
136
|
/** input str: from url */
|
|
138
|
-
readonly page: import("valibot").
|
|
137
|
+
readonly page: import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").TransformAction<string, number>, import("valibot").SchemaWithPipe<[import("valibot").NumberSchema<undefined>, import("valibot").IntegerAction<number, undefined>, import("valibot").MinValueAction<number, 1, undefined>]>]>;
|
|
139
138
|
}, undefined>;
|
|
140
|
-
export declare const
|
|
141
|
-
readonly
|
|
139
|
+
export declare const funds_npo_memberof_search: import("valibot").ObjectSchema<{
|
|
140
|
+
readonly npo_profile_featured: import("valibot").OptionalSchema<import("valibot").SchemaWithPipe<[import("valibot").SchemaWithPipe<[import("valibot").StringSchema<undefined>, import("valibot").TrimAction]>, import("valibot").TransformAction<string, boolean>, import("valibot").BooleanSchema<undefined>]>, never>;
|
|
142
141
|
}, undefined>;
|
|
143
|
-
export interface
|
|
144
|
-
}
|
|
145
|
-
export interface FundUpdate extends InferOutput<typeof fundUpdate> {
|
|
146
|
-
}
|
|
147
|
-
export interface FundsParams extends InferOutput<typeof fundsParams> {
|
|
142
|
+
export interface IFundNew extends InferOutput<typeof fund_new> {
|
|
148
143
|
}
|
|
149
|
-
export interface
|
|
144
|
+
export interface IFundUpdate extends InferOutput<typeof fund_update> {
|
|
150
145
|
}
|
|
151
|
-
export interface
|
|
152
|
-
hide_bg_tip: boolean;
|
|
153
|
-
donateMethods?: DonateMethodId[];
|
|
146
|
+
export interface IFundsSearchObj extends InferOutput<typeof funds_search> {
|
|
154
147
|
}
|
|
155
|
-
export interface
|
|
156
|
-
/** uuid */
|
|
157
|
-
id: string;
|
|
158
|
-
env: Environment;
|
|
159
|
-
/** fund can be closed before expiration */
|
|
160
|
-
active: boolean;
|
|
161
|
-
verified: boolean;
|
|
162
|
-
/** to date received: initialized to `0` */
|
|
163
|
-
donation_total_usd: number;
|
|
164
|
-
/** "{number}" - endow id, "{email} - user*/
|
|
165
|
-
creator_id: string;
|
|
166
|
-
creator_name: string;
|
|
167
|
-
settings: FundSettings;
|
|
148
|
+
export interface IFundsNpoMemberOfSearchObj extends InferOutput<typeof funds_npo_memberof_search> {
|
|
168
149
|
}
|
package/dist/schema.mjs
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { slug } from "@better-giving/
|
|
2
|
-
import { url, array, boolean, integer, isoTimestamp, literal, maxLength, minValue, nonEmpty, number, object, optional, partial, pick, pipe, string, transform,
|
|
3
|
-
|
|
4
|
-
export const
|
|
1
|
+
import { $, $int_gte1, slug } from "@better-giving/schemas";
|
|
2
|
+
import { url, array, boolean, integer, isoTimestamp, literal, maxLength, minValue, nonEmpty, number, object, optional, partial, pick, pipe, string, transform, union, uuid, } from "valibot";
|
|
3
|
+
export { slug } from "@better-giving/schemas";
|
|
4
|
+
export const fund_id = pipe($, uuid());
|
|
5
5
|
/**
|
|
6
6
|
* when fundraiser is created in the context of an NPO, all members of that NPO can edit the fundraiser
|
|
7
7
|
* 0 - none
|
|
8
8
|
*/
|
|
9
9
|
export const npo_owner = pipe(number(), integer(), minValue(0));
|
|
10
|
-
export const
|
|
11
|
-
name: pipe(
|
|
12
|
-
description: pipe(
|
|
13
|
-
banner: pipe(
|
|
14
|
-
logo: pipe(
|
|
10
|
+
export const fund_new = object({
|
|
11
|
+
name: pipe($, nonEmpty("required")),
|
|
12
|
+
description: pipe($, nonEmpty("required")),
|
|
13
|
+
banner: pipe($, url()),
|
|
14
|
+
logo: pipe($, url()),
|
|
15
15
|
/** endowment ids */
|
|
16
16
|
members: pipe(array(pipe(number(), integer(), minValue(1))), nonEmpty(), maxLength(10)),
|
|
17
17
|
featured: boolean(),
|
|
18
|
-
expiration: optional(pipe(
|
|
18
|
+
expiration: optional(pipe($, isoTimestamp("invalid date"), minValue(new Date().toISOString()) //created each parsing
|
|
19
19
|
)),
|
|
20
20
|
/** `"0"` - none, {"number"} = fixed */
|
|
21
21
|
target: union([
|
|
22
22
|
literal("smart"),
|
|
23
23
|
pipe(string(), transform((v) => +v), number(), minValue(0), transform((v) => v.toString())),
|
|
24
24
|
]),
|
|
25
|
-
videos: array(pipe(
|
|
25
|
+
videos: array(pipe($, url())),
|
|
26
26
|
npo_owner,
|
|
27
27
|
slug: optional(slug),
|
|
28
28
|
});
|
|
29
|
-
export const
|
|
29
|
+
export const fund_update = partial(pick(fund_new, [
|
|
30
30
|
"name",
|
|
31
31
|
"description",
|
|
32
32
|
"banner",
|
|
@@ -36,13 +36,13 @@ export const fundUpdate = partial(pick(newFund, [
|
|
|
36
36
|
"videos",
|
|
37
37
|
"slug",
|
|
38
38
|
]));
|
|
39
|
-
export const
|
|
39
|
+
export const funds_search = object({
|
|
40
40
|
/** search text */
|
|
41
|
-
query: optional(
|
|
41
|
+
query: optional($),
|
|
42
42
|
/** input str: from url */
|
|
43
|
-
page:
|
|
43
|
+
page: $int_gte1,
|
|
44
44
|
});
|
|
45
|
-
export const
|
|
45
|
+
export const funds_npo_memberof_search = object({
|
|
46
46
|
/*
|
|
47
47
|
* this endow is the only member (not an index fund),
|
|
48
48
|
* and is approved:
|
|
@@ -51,5 +51,5 @@ export const fundsEndowMemberOfParams = object({
|
|
|
51
51
|
*
|
|
52
52
|
* input str: from url
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
npo_profile_featured: optional(pipe($, transform((x) => x === "true"), boolean())),
|
|
55
55
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-giving/fundraiser",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.7",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"valibot": "0.42.0",
|
|
6
|
-
"@better-giving/endowment": "4.0.5",
|
|
7
6
|
"@better-giving/schemas": "2.0.0",
|
|
8
|
-
"@better-giving/types": "1.1.8"
|
|
7
|
+
"@better-giving/types": "1.1.8",
|
|
8
|
+
"@better-giving/db": "2.0.1",
|
|
9
|
+
"@aws-sdk/lib-dynamodb": "3.485.0"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {
|
|
11
12
|
"@better-giving/config": "1.1.2"
|
package/src/cloudsearch.mts
CHANGED
|
@@ -3,11 +3,12 @@ import type {
|
|
|
3
3
|
ToHitFields,
|
|
4
4
|
ToUpdate,
|
|
5
5
|
} from "@better-giving/types/cloudsearch";
|
|
6
|
-
import type {
|
|
6
|
+
import type { IFundInternal } from "./interfaces.mjs";
|
|
7
|
+
import type { IFundNew } from "./schema.mjs";
|
|
7
8
|
|
|
8
9
|
export interface CloudsearchFund
|
|
9
10
|
extends Pick<
|
|
10
|
-
|
|
11
|
+
IFundNew,
|
|
11
12
|
| "name"
|
|
12
13
|
| "description"
|
|
13
14
|
| "logo"
|
|
@@ -17,7 +18,7 @@ export interface CloudsearchFund
|
|
|
17
18
|
| "target"
|
|
18
19
|
>,
|
|
19
20
|
Pick<
|
|
20
|
-
|
|
21
|
+
IFundInternal,
|
|
21
22
|
| "id"
|
|
22
23
|
| "env"
|
|
23
24
|
| "active"
|
package/src/db.mts
CHANGED
|
@@ -1,20 +1,78 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Db, UpdateBuilder, type TxType } from "@better-giving/db";
|
|
2
|
+
import type { IFund } from "./interfaces.mjs";
|
|
3
|
+
import {
|
|
4
|
+
QueryCommand,
|
|
5
|
+
UpdateCommand,
|
|
6
|
+
type QueryCommandInput,
|
|
7
|
+
} from "@aws-sdk/lib-dynamodb";
|
|
8
|
+
import { UUID_REGEX } from "valibot";
|
|
9
|
+
import type { IFundUpdate } from "./schema.mjs";
|
|
2
10
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
11
|
+
export class FundDb extends Db {
|
|
12
|
+
static readonly table = "funds";
|
|
13
|
+
static readonly slug_env_gsi = "slug-env-gsi";
|
|
14
|
+
key_fund(id: string) {
|
|
15
|
+
return {
|
|
16
|
+
PK: `Fund#${id}`,
|
|
17
|
+
SK: `Fund#${id}`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
fund_record(data: IFund) {
|
|
21
|
+
return {
|
|
22
|
+
...this.key_fund(data.id),
|
|
23
|
+
...data,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
fund_put_txi(data: IFund): TxType["Put"] {
|
|
27
|
+
return {
|
|
28
|
+
TableName: FundDb.table,
|
|
29
|
+
Item: this.fund_record(data),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**@param id - slug or uuid */
|
|
33
|
+
async fund(id: string): Promise<IFund | undefined> {
|
|
34
|
+
const q: QueryCommandInput = {
|
|
35
|
+
TableName: FundDb.table,
|
|
36
|
+
Limit: 1,
|
|
37
|
+
};
|
|
38
|
+
if (UUID_REGEX.test(id)) {
|
|
39
|
+
q.KeyConditionExpression = "PK = :PK AND SK = :SK";
|
|
40
|
+
q.ExpressionAttributeValues = this.key_fund(id);
|
|
41
|
+
} else {
|
|
42
|
+
q.IndexName = FundDb.slug_env_gsi;
|
|
43
|
+
q.KeyConditionExpression = "slug = :slug AND env = :env";
|
|
44
|
+
q.ExpressionAttributeValues = {
|
|
45
|
+
":slug": id,
|
|
46
|
+
":env": this.env,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
6
49
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
50
|
+
const { Items: i = [] } = await this.client.send(new QueryCommand(q));
|
|
51
|
+
return i[0] && this.sans_keys(i[0]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async fund_edit(id: string, { target, slug, ...update }: IFundUpdate) {
|
|
55
|
+
const updates = new UpdateBuilder();
|
|
56
|
+
|
|
57
|
+
if (slug) updates.set("slug", slug);
|
|
58
|
+
if (slug === "") updates.remove("slug");
|
|
59
|
+
|
|
60
|
+
if (target || target === "0") {
|
|
61
|
+
updates.set("target", target);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const [k, v] of Object.entries(update)) {
|
|
65
|
+
if (v === undefined) continue;
|
|
66
|
+
updates.set(k, v);
|
|
67
|
+
}
|
|
12
68
|
|
|
13
|
-
|
|
14
|
-
|
|
69
|
+
const command = new UpdateCommand({
|
|
70
|
+
TableName: FundDb.table,
|
|
71
|
+
Key: this.key_fund(id),
|
|
72
|
+
ReturnValues: "ALL_NEW",
|
|
73
|
+
...updates.collect(),
|
|
74
|
+
});
|
|
15
75
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export interface Keys extends Pick<Attributes, "slug" | "env"> {}
|
|
19
|
-
export interface Record extends DbRecord {} //all attributes are copied to this index
|
|
76
|
+
return this.client.send(command).then((res) => res.Attributes ?? {});
|
|
77
|
+
}
|
|
20
78
|
}
|
package/src/index.mts
CHANGED
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import type { CloudsearchFund as FundItem } from "./cloudsearch.mjs";
|
|
3
|
-
import type { FundInternal, NewFund } from "./schema.mjs";
|
|
1
|
+
export type { CloudsearchFund as FundItem } from "./cloudsearch.mjs";
|
|
4
2
|
export type {
|
|
3
|
+
IFundNew,
|
|
4
|
+
IFundUpdate,
|
|
5
|
+
IFundsNpoMemberOfSearchObj,
|
|
6
|
+
IFundsSearchObj,
|
|
7
|
+
DonateMethodId,
|
|
5
8
|
Environment,
|
|
6
|
-
FundSettings,
|
|
7
|
-
FundUpdate,
|
|
8
|
-
FundsEndowMemberOfParams,
|
|
9
|
-
FundsParams,
|
|
10
|
-
NewFund,
|
|
11
9
|
} from "./schema.mjs";
|
|
12
|
-
export type { FundItem };
|
|
13
|
-
|
|
14
|
-
export interface FundMember {
|
|
15
|
-
id: number;
|
|
16
|
-
name: string;
|
|
17
|
-
logo: string | undefined;
|
|
18
|
-
banner: string | undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface SingleFund extends Omit<NewFund, "members">, FundInternal {
|
|
22
|
-
members: FundMember[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface FundsPage extends IPageNumbered<FundItem> {}
|
|
26
10
|
|
|
27
11
|
export const MAX_EXPIRATION = "9999-12-31T23:59:59Z";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DonateMethodId, Environment } from "@better-giving/schemas";
|
|
2
|
+
import type { IFundNew } from "./schema.mjs";
|
|
3
|
+
import type { IPageNumbered } from "@better-giving/types/api";
|
|
4
|
+
import type { CloudsearchFund } from "./cloudsearch.mjs";
|
|
5
|
+
|
|
6
|
+
export interface IFundSettings {
|
|
7
|
+
hide_bg_tip: boolean;
|
|
8
|
+
donateMethods?: DonateMethodId[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface IFundCreator {
|
|
12
|
+
/** "{number}" - endow id, "{email} - user*/
|
|
13
|
+
creator_id: string;
|
|
14
|
+
creator_name: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IFundInternal extends IFundCreator {
|
|
18
|
+
/** uuid */
|
|
19
|
+
id: string;
|
|
20
|
+
env: Environment;
|
|
21
|
+
/** fund can be closed before expiration */
|
|
22
|
+
active: boolean;
|
|
23
|
+
verified: boolean;
|
|
24
|
+
/** to date received: initialized to `0` */
|
|
25
|
+
donation_total_usd: number;
|
|
26
|
+
settings: IFundSettings;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IFund extends IFundNew, IFundInternal {}
|
|
30
|
+
|
|
31
|
+
export interface IFundsPage extends IPageNumbered<CloudsearchFund> {}
|
package/src/schema.mts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { slug } from "@better-giving/
|
|
2
|
-
import type { DonateMethodId } from "@better-giving/schemas";
|
|
3
|
-
import type { Environment } from "@better-giving/types/list";
|
|
4
|
-
export type { Environment } from "@better-giving/types/list";
|
|
1
|
+
import { $, $int_gte1, slug } from "@better-giving/schemas";
|
|
5
2
|
import {
|
|
6
3
|
url,
|
|
7
4
|
type InferOutput,
|
|
@@ -21,25 +18,25 @@ import {
|
|
|
21
18
|
pipe,
|
|
22
19
|
string,
|
|
23
20
|
transform,
|
|
24
|
-
trim,
|
|
25
21
|
union,
|
|
26
22
|
uuid,
|
|
27
23
|
} from "valibot";
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
export { type DonateMethodId, slug } from "@better-giving/schemas";
|
|
26
|
+
export type { Environment } from "@better-giving/types/list";
|
|
30
27
|
|
|
31
|
-
export const
|
|
28
|
+
export const fund_id = pipe($, uuid());
|
|
32
29
|
/**
|
|
33
30
|
* when fundraiser is created in the context of an NPO, all members of that NPO can edit the fundraiser
|
|
34
31
|
* 0 - none
|
|
35
32
|
*/
|
|
36
33
|
export const npo_owner = pipe(number(), integer(), minValue(0));
|
|
37
34
|
|
|
38
|
-
export const
|
|
39
|
-
name: pipe(
|
|
40
|
-
description: pipe(
|
|
41
|
-
banner: pipe(
|
|
42
|
-
logo: pipe(
|
|
35
|
+
export const fund_new = object({
|
|
36
|
+
name: pipe($, nonEmpty("required")),
|
|
37
|
+
description: pipe($, nonEmpty("required")),
|
|
38
|
+
banner: pipe($, url()),
|
|
39
|
+
logo: pipe($, url()),
|
|
43
40
|
/** endowment ids */
|
|
44
41
|
members: pipe(
|
|
45
42
|
array(pipe(number(), integer(), minValue(1))),
|
|
@@ -49,7 +46,7 @@ export const newFund = object({
|
|
|
49
46
|
featured: boolean(),
|
|
50
47
|
expiration: optional(
|
|
51
48
|
pipe(
|
|
52
|
-
|
|
49
|
+
$,
|
|
53
50
|
isoTimestamp("invalid date"),
|
|
54
51
|
minValue(new Date().toISOString()) //created each parsing
|
|
55
52
|
)
|
|
@@ -65,13 +62,13 @@ export const newFund = object({
|
|
|
65
62
|
transform((v) => v.toString())
|
|
66
63
|
),
|
|
67
64
|
]),
|
|
68
|
-
videos: array(pipe(
|
|
65
|
+
videos: array(pipe($, url())),
|
|
69
66
|
npo_owner,
|
|
70
67
|
slug: optional(slug),
|
|
71
68
|
});
|
|
72
69
|
|
|
73
|
-
export const
|
|
74
|
-
pick(
|
|
70
|
+
export const fund_update = partial(
|
|
71
|
+
pick(fund_new, [
|
|
75
72
|
"name",
|
|
76
73
|
"description",
|
|
77
74
|
"banner",
|
|
@@ -83,22 +80,14 @@ export const fundUpdate = partial(
|
|
|
83
80
|
])
|
|
84
81
|
);
|
|
85
82
|
|
|
86
|
-
export const
|
|
83
|
+
export const funds_search = object({
|
|
87
84
|
/** search text */
|
|
88
|
-
query: optional(
|
|
85
|
+
query: optional($),
|
|
89
86
|
/** input str: from url */
|
|
90
|
-
page:
|
|
91
|
-
pipe(
|
|
92
|
-
str,
|
|
93
|
-
transform((x) => +x),
|
|
94
|
-
number(),
|
|
95
|
-
integer(),
|
|
96
|
-
minValue(1)
|
|
97
|
-
)
|
|
98
|
-
),
|
|
87
|
+
page: $int_gte1,
|
|
99
88
|
});
|
|
100
89
|
|
|
101
|
-
export const
|
|
90
|
+
export const funds_npo_memberof_search = object({
|
|
102
91
|
/*
|
|
103
92
|
* this endow is the only member (not an index fund),
|
|
104
93
|
* and is approved:
|
|
@@ -107,37 +96,17 @@ export const fundsEndowMemberOfParams = object({
|
|
|
107
96
|
*
|
|
108
97
|
* input str: from url
|
|
109
98
|
*/
|
|
110
|
-
|
|
99
|
+
npo_profile_featured: optional(
|
|
111
100
|
pipe(
|
|
112
|
-
|
|
101
|
+
$,
|
|
113
102
|
transform((x) => x === "true"),
|
|
114
103
|
boolean()
|
|
115
104
|
)
|
|
116
105
|
),
|
|
117
106
|
});
|
|
118
107
|
|
|
119
|
-
export interface
|
|
120
|
-
export interface
|
|
121
|
-
export interface
|
|
122
|
-
export interface
|
|
123
|
-
extends InferOutput<typeof
|
|
124
|
-
|
|
125
|
-
export interface FundSettings {
|
|
126
|
-
hide_bg_tip: boolean;
|
|
127
|
-
donateMethods?: DonateMethodId[];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface FundInternal {
|
|
131
|
-
/** uuid */
|
|
132
|
-
id: string;
|
|
133
|
-
env: Environment;
|
|
134
|
-
/** fund can be closed before expiration */
|
|
135
|
-
active: boolean;
|
|
136
|
-
verified: boolean;
|
|
137
|
-
/** to date received: initialized to `0` */
|
|
138
|
-
donation_total_usd: number;
|
|
139
|
-
/** "{number}" - endow id, "{email} - user*/
|
|
140
|
-
creator_id: string;
|
|
141
|
-
creator_name: string;
|
|
142
|
-
settings: FundSettings;
|
|
143
|
-
}
|
|
108
|
+
export interface IFundNew extends InferOutput<typeof fund_new> {}
|
|
109
|
+
export interface IFundUpdate extends InferOutput<typeof fund_update> {}
|
|
110
|
+
export interface IFundsSearchObj extends InferOutput<typeof funds_search> {}
|
|
111
|
+
export interface IFundsNpoMemberOfSearchObj
|
|
112
|
+
extends InferOutput<typeof funds_npo_memberof_search> {}
|