@seedprotocol/feed 0.4.31 → 0.5.1
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/README.md +36 -0
- package/dist/bootstrap.d.ts +13 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/cache/config.d.ts.map +1 -1
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/getFeedItems.d.ts.map +1 -1
- package/dist/index.d.ts +9 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +556 -525
- package/dist/index.js.map +1 -1
- package/dist/services/arweaveImageService.d.ts.map +1 -1
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Generates **RSS 2.0**, **Atom**, and **JSON Feed** from Seed items. For EAS-assembled feeds, `getFeedItemsBySchemaName` marks relation fields with `_feedFieldStorageModels` / `_feedListElementStorageModels` so `pickFeedItemContent` prefers **html** / **file** / **json** storage relations before the legacy `html` / `body` / `content` chain; feed output is **not** sanitized—see [FEED_RICH_FIELDS.md](../../docs/FEED_RICH_FIELDS.md) (including **Publishing feeds** and trust boundaries).
|
|
4
4
|
|
|
5
|
+
## P2P distribution
|
|
6
|
+
|
|
7
|
+
HTTP hosting is optional. To publish the same generated feeds onto Hyperdrive / Hyperswarm (and serve them to classic RSS readers over localhost), use **`@seedprotocol/feed-hyper`** and the `seed feed` CLI. See [packages/feed-hyper/README.md](../feed-hyper/README.md) and [docs/FEED_HYPER.md](../../docs/FEED_HYPER.md).
|
|
8
|
+
|
|
9
|
+
Channel self-links use `feedUrl` from site config (`setSiteConfig` / `createFeed` overrides). Home/channel links use `siteUrl`.
|
|
10
|
+
|
|
5
11
|
## Configuration
|
|
6
12
|
|
|
7
13
|
### Revoked attestations
|
|
@@ -12,6 +18,33 @@ Revoked attestations are excluded from feed queries by default. Items that have
|
|
|
12
18
|
|
|
13
19
|
In development (`NODE_ENV=development`), feed caching is **disabled by default** so you always see fresh content. Set `CACHE_ENABLED=true` to enable caching in dev. See `packages/feed/src/cache/README.md` for full cache configuration.
|
|
14
20
|
|
|
21
|
+
### Arweave gateway URLs
|
|
22
|
+
|
|
23
|
+
Feed generation resolves Arweave transaction IDs to gateway URLs (RSS enclosures, image relations, rich-text hydration). Configure the primary gateway with any of these (first match wins):
|
|
24
|
+
|
|
25
|
+
- `ARWEAVE_HOST` — server-side (Node, Bun, etc.)
|
|
26
|
+
- `NEXT_PUBLIC_ARWEAVE_HOST` — Next.js / universal apps
|
|
27
|
+
- `VITE_ARWEAVE_HOST` — Vite apps (also set this for Bun if you use a `VITE_`-prefixed `.env`)
|
|
28
|
+
|
|
29
|
+
Or pass an explicit domain at init time (takes precedence over env):
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { initializeFeedPlatform } from '@seedprotocol/feed'
|
|
33
|
+
|
|
34
|
+
await initializeFeedPlatform({ arweaveDomain: 'arweave.net' })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
When unset, the default primary gateway is `ar.seedprotocol.io`.
|
|
38
|
+
|
|
39
|
+
**Read fallbacks** (image metadata probing, gateway health checks) use additional public gateways. To override the fallback list:
|
|
40
|
+
|
|
41
|
+
- `ARWEAVE_READ_GATEWAYS` or `NEXT_PUBLIC_ARWEAVE_READ_GATEWAYS` — comma-separated hostnames
|
|
42
|
+
- `IMAGE_METADATA_GATEWAYS` — comma-separated hostnames for RSS image dimension detection
|
|
43
|
+
|
|
44
|
+
When a custom primary gateway is configured, `ar.seedprotocol.io` is omitted from automatic fallbacks (unless you include it explicitly in one of the env lists above).
|
|
45
|
+
|
|
46
|
+
**Note:** Items already stored with full `https://ar.seedprotocol.io/...` URLs are passed through unchanged. Only newly resolved transaction IDs use the configured gateway.
|
|
47
|
+
|
|
15
48
|
### Feed Item URLs (EASScan attestation links)
|
|
16
49
|
|
|
17
50
|
Item links in the feed can point to EASScan attestation pages. Set these environment variables:
|
|
@@ -25,6 +58,9 @@ Item links in the feed can point to EASScan attestation pages. Set these environ
|
|
|
25
58
|
### Example .env
|
|
26
59
|
|
|
27
60
|
```bash
|
|
61
|
+
# Arweave gateway for feed media / relation URLs
|
|
62
|
+
ARWEAVE_HOST=arweave.net
|
|
63
|
+
|
|
28
64
|
# Default: item links use https://easscan.org/attestation/view/{uid}
|
|
29
65
|
# Override for testnet:
|
|
30
66
|
FEED_ITEM_URL_BASE=https://optimism-sepolia.easscan.org
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type InitializeFeedPlatformOptions = {
|
|
2
|
+
arweaveDomain?: string;
|
|
3
|
+
};
|
|
4
|
+
/** Register Node EAS + Arweave clients for feed generation (no full SDK client). */
|
|
5
|
+
export declare function initializeFeedPlatform(options?: InitializeFeedPlatformOptions): Promise<void>;
|
|
6
|
+
/** @deprecated Use initializeFeedPlatform */
|
|
7
|
+
export declare const initializeSeedClient: typeof initializeFeedPlatform;
|
|
8
|
+
export declare function getClient(): Promise<{
|
|
9
|
+
isInitialized: () => boolean;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function teardownSeedClient(): Promise<void>;
|
|
12
|
+
export { DEFAULT_ARWEAVE_HOST } from '@seedprotocol/arweave';
|
|
13
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAMD,oFAAoF;AACpF,wBAAsB,sBAAsB,CAC1C,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,+BAAyB,CAAA;AAE1D,wBAAsB,SAAS,IAAI,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,OAAO,CAAA;CAAE,CAAC,CAG3E;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAExD;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cache/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cache/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,SAAS,CAAC;AAEhE;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,WAAW,CA0E7C"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
import type { FeedConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Partial site/feed metadata overrides. Unspecified fields keep current/default values.
|
|
4
|
+
* `author` fields are merged individually when provided.
|
|
5
|
+
*/
|
|
6
|
+
export type SiteConfigOverrides = Partial<Omit<FeedConfig, 'author'>> & {
|
|
7
|
+
author?: Partial<NonNullable<FeedConfig['author']>>;
|
|
8
|
+
};
|
|
9
|
+
/** Hardcoded Seed Protocol defaults for feed channel metadata. */
|
|
10
|
+
export declare const DEFAULT_SITE_CONFIG: FeedConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Merge overrides onto a base site config. Missing fields keep the base values.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveSiteConfig(overrides?: SiteConfigOverrides, base?: FeedConfig): FeedConfig;
|
|
15
|
+
/** Current site/feed channel config (defaults until `setSiteConfig` is called). */
|
|
16
|
+
export declare function getSiteConfig(): FeedConfig;
|
|
17
|
+
/**
|
|
18
|
+
* Override site/feed channel metadata for this process.
|
|
19
|
+
* Partial values are merged onto the Seed Protocol defaults (not the previous override set).
|
|
20
|
+
*/
|
|
21
|
+
export declare function setSiteConfig(overrides: SiteConfigOverrides): FeedConfig;
|
|
22
|
+
/** Restore hardcoded Seed Protocol site config defaults. */
|
|
23
|
+
export declare function resetSiteConfig(): void;
|
|
1
24
|
/**
|
|
2
25
|
* Load feed configuration from environment variables
|
|
3
26
|
*
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,IAAI;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,YAAY,GAAG,eAAe,CAAC;CACvD,CAcA"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,GAAG;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;CACrD,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,mBAAmB,EAAE,UAYjC,CAAC;AAWF;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,CAAC,EAAE,mBAAmB,EAC/B,IAAI,GAAE,UAAuB,GAC5B,UAAU,CAoBZ;AAED,mFAAmF;AACnF,wBAAgB,aAAa,IAAI,UAAU,CAE1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,UAAU,CAGxE;AAED,4DAA4D;AAC5D,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,IAAI;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,YAAY,GAAG,eAAe,CAAC;CACvD,CAcA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFeedItems.d.ts","sourceRoot":"","sources":["../src/getFeedItems.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"getFeedItems.d.ts","sourceRoot":"","sources":["../src/getFeedItems.ts"],"names":[],"mappings":"AAkpBA,eAAO,MAAM,wBAAwB,GACnC,YAAY,MAAM,EAClB,UAAU;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAC1C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CASnC,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAC3C,YAAY,MAAM,EAClB,MAAM,MAAM,EACZ,OAAO,MAAM,KACZ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAoCnC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';
|
|
2
|
-
export { loadFeedConfig } from './config';
|
|
2
|
+
export { loadFeedConfig, DEFAULT_SITE_CONFIG, getSiteConfig, setSiteConfig, resetSiteConfig, resolveSiteConfig, type SiteConfigOverrides, } from './config';
|
|
3
3
|
export { parseRssString, type ParsedRssChannel } from './consume/parseRss';
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
4
|
+
export type { FeedConfig, FeedFormat, GraphQLItem } from './types';
|
|
5
|
+
export { initializeFeedPlatform, initializeSeedClient, getClient, teardownSeedClient, DEFAULT_ARWEAVE_HOST, } from './bootstrap';
|
|
6
|
+
export { classifyMediaRef, normalizeFeedItemFields, getFeedItemStringField, } from '@seedprotocol/arweave';
|
|
7
|
+
export type { FeedFieldManifest, FeedFieldDescriptor, FeedFieldRole, ClassifyMediaRefOptions, MediaRefClassification, ResolveMediaRefResult, ResolveMediaRefOptions, NormalizedMediaField, NormalizedHtmlField, NormalizedTextField, NormalizedFeedFieldValue, } from '@seedprotocol/arweave';
|
|
6
8
|
import type { FeedFormat, GraphQLItem, TransformOptions } from './types';
|
|
7
9
|
export type { TransformOptions } from './types';
|
|
10
|
+
import { type SiteConfigOverrides } from './config';
|
|
8
11
|
export { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';
|
|
9
12
|
export { pickFeedItemContent, pickFeedItemDescription, feedItemRichTextContainsDataUriImage, filterItemsByRichTextDataUriImagePolicy, } from './pickFeedItemRichText';
|
|
10
13
|
export { FEED_RICH_BODY_STORAGE_SCHEMAS, isFeedRichBodyStorageSchema, } from './feedFieldStorageModel';
|
|
@@ -12,21 +15,6 @@ export { FEED_RICH_BODY_STORAGE_SCHEMAS, isFeedRichBodyStorageSchema, } from './
|
|
|
12
15
|
* Reset cache manager (useful for testing)
|
|
13
16
|
*/
|
|
14
17
|
export declare function resetCacheManager(): void;
|
|
15
|
-
/**
|
|
16
|
-
* Initialize the Seed Protocol client
|
|
17
|
-
* This should be called as soon as the app is ready
|
|
18
|
-
*/
|
|
19
|
-
export declare const initializeSeedClient: () => Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Get the Seed Protocol client, initializing it if necessary
|
|
22
|
-
* This function can be called from any context (Electron main process or Vite dev server)
|
|
23
|
-
*/
|
|
24
|
-
export declare const getClient: () => Promise<any>;
|
|
25
|
-
/**
|
|
26
|
-
* Teardown the Seed Protocol client
|
|
27
|
-
* This should be called when the app is quitting
|
|
28
|
-
*/
|
|
29
|
-
export declare const teardownSeedClient: () => Promise<void>;
|
|
30
18
|
export interface FeedPaginationOptions {
|
|
31
19
|
page: number;
|
|
32
20
|
pageSize: number;
|
|
@@ -37,7 +25,7 @@ export interface FeedArchiveLink {
|
|
|
37
25
|
rel: string;
|
|
38
26
|
href: string;
|
|
39
27
|
}
|
|
40
|
-
export declare const createFeed: (items: GraphQLItem[], schemaName: string, format: FeedFormat, cacheBust?: string, pagination?: FeedPaginationOptions, archiveLinks?: FeedArchiveLink[], isArchive?: boolean, transformOverrides?: Partial<TransformOptions
|
|
28
|
+
export declare const createFeed: (items: GraphQLItem[], schemaName: string, format: FeedFormat, cacheBust?: string, pagination?: FeedPaginationOptions, archiveLinks?: FeedArchiveLink[], isArchive?: boolean, transformOverrides?: Partial<TransformOptions>, siteConfigOverrides?: SiteConfigOverrides) => Promise<string>;
|
|
41
29
|
/**
|
|
42
30
|
* Main route handler for feed generation.
|
|
43
31
|
*
|
|
@@ -48,7 +36,7 @@ export declare const createFeed: (items: GraphQLItem[], schemaName: string, form
|
|
|
48
36
|
* - /identities/json → JSON feed of identities
|
|
49
37
|
* - /posts/rss?v=1234567890 → RSS feed with cache busting parameter
|
|
50
38
|
*/
|
|
51
|
-
export declare function handleFeedRequest(collectionSegment: string, formatSegment: string, ifNoneMatch?: string | null, cacheBust?: string, page?: number): Promise<Response>;
|
|
39
|
+
export declare function handleFeedRequest(collectionSegment: string, formatSegment: string, ifNoneMatch?: string | null, cacheBust?: string, page?: number, siteConfigOverrides?: SiteConfigOverrides): Promise<Response>;
|
|
52
40
|
/**
|
|
53
41
|
* Route handler for monthly archive feeds (RFC 5005).
|
|
54
42
|
*
|
|
@@ -56,5 +44,5 @@ export declare function handleFeedRequest(collectionSegment: string, formatSegme
|
|
|
56
44
|
* Examples:
|
|
57
45
|
* - /posts/archive/2024/02/rss → RSS feed of posts from February 2024
|
|
58
46
|
*/
|
|
59
|
-
export declare function handleArchiveFeedRequest(collectionSegment: string, year: number, month: number, formatSegment: string, ifNoneMatch?: string | null, cacheBust?: string): Promise<Response>;
|
|
47
|
+
export declare function handleArchiveFeedRequest(collectionSegment: string, year: number, month: number, formatSegment: string, ifNoneMatch?: string | null, cacheBust?: string, siteConfigOverrides?: SiteConfigOverrides): Promise<Response>;
|
|
60
48
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,KAAK,mBAAmB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAA6B,MAAM,SAAS,CAAC;AAEpG,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAKhD,OAAO,EAAqC,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAC;AASvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,oCAAoC,EACpC,uCAAuC,GACxC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,yBAAyB,CAAC;AAiOjC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAiLD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACb;AAED,eAAO,MAAM,UAAU,GACrB,OAAO,WAAW,EAAE,EACpB,YAAY,MAAM,EAClB,QAAQ,UAAU,EAClB,YAAY,MAAM,EAClB,aAAa,qBAAqB,EAClC,eAAe,eAAe,EAAE,EAChC,YAAY,OAAO,EACnB,qBAAqB,OAAO,CAAC,gBAAgB,CAAC,EAC9C,sBAAsB,mBAAmB,KACxC,OAAO,CAAC,MAAM,CAmQhB,CAAA;AA8BD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,CAAC,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,mBAAmB,GACxC,OAAO,CAAC,QAAQ,CAAC,CA4NnB;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,iBAAiB,EAAE,MAAM,EACzB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,CAAC,EAAE,MAAM,EAClB,mBAAmB,CAAC,EAAE,mBAAmB,GACxC,OAAO,CAAC,QAAQ,CAAC,CAiInB"}
|