@atproto/bsky 0.0.198 → 0.0.199
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/CHANGELOG.md +14 -0
- package/dist/api/age-assurance/const.d.ts +11 -0
- package/dist/api/age-assurance/const.d.ts.map +1 -0
- package/dist/api/age-assurance/const.js +142 -0
- package/dist/api/age-assurance/const.js.map +1 -0
- package/dist/api/age-assurance/index.d.ts +4 -0
- package/dist/api/age-assurance/index.d.ts.map +1 -0
- package/dist/api/age-assurance/index.js +24 -0
- package/dist/api/age-assurance/index.js.map +1 -0
- package/dist/api/age-assurance/kws/age-verified.d.ts +109 -0
- package/dist/api/age-assurance/kws/age-verified.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/age-verified.js +63 -0
- package/dist/api/age-assurance/kws/age-verified.js.map +1 -0
- package/dist/api/age-assurance/kws/const.d.ts +13 -0
- package/dist/api/age-assurance/kws/const.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/const.js +36 -0
- package/dist/api/age-assurance/kws/const.js.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.d.ts +75 -0
- package/dist/api/age-assurance/kws/external-payload.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.js +124 -0
- package/dist/api/age-assurance/kws/external-payload.js.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.test.d.ts +2 -0
- package/dist/api/age-assurance/kws/external-payload.test.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.test.js +65 -0
- package/dist/api/age-assurance/kws/external-payload.test.js.map +1 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.d.ts +4 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.d.ts.map +1 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.js +76 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.js.map +1 -0
- package/dist/api/age-assurance/stash.d.ts +4 -0
- package/dist/api/age-assurance/stash.d.ts.map +1 -0
- package/dist/api/age-assurance/stash.js +19 -0
- package/dist/api/age-assurance/stash.js.map +1 -0
- package/dist/api/age-assurance/types.d.ts +10 -0
- package/dist/api/age-assurance/types.d.ts.map +1 -0
- package/dist/api/age-assurance/types.js +3 -0
- package/dist/api/age-assurance/types.js.map +1 -0
- package/dist/api/age-assurance/util.d.ts +15 -0
- package/dist/api/age-assurance/util.d.ts.map +1 -0
- package/dist/api/age-assurance/util.js +54 -0
- package/dist/api/age-assurance/util.js.map +1 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.d.ts +4 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.d.ts.map +1 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.js +63 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.js.map +1 -0
- package/dist/api/app/bsky/ageassurance/begin.d.ts +4 -0
- package/dist/api/app/bsky/ageassurance/begin.d.ts.map +1 -0
- package/dist/api/app/bsky/ageassurance/begin.js +131 -0
- package/dist/api/app/bsky/ageassurance/begin.js.map +1 -0
- package/dist/api/app/bsky/ageassurance/getConfig.d.ts +4 -0
- package/dist/api/app/bsky/ageassurance/getConfig.d.ts.map +1 -0
- package/dist/api/app/bsky/ageassurance/getConfig.js +16 -0
- package/dist/api/app/bsky/ageassurance/getConfig.js.map +1 -0
- package/dist/api/app/bsky/ageassurance/getState.d.ts +4 -0
- package/dist/api/app/bsky/ageassurance/getState.d.ts.map +1 -0
- package/dist/api/app/bsky/ageassurance/getState.js +42 -0
- package/dist/api/app/bsky/ageassurance/getState.js.map +1 -0
- package/dist/api/external.d.ts.map +1 -1
- package/dist/api/external.js +2 -0
- package/dist/api/external.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +8 -2
- package/dist/api/index.js.map +1 -1
- package/dist/api/kws/api.d.ts.map +1 -1
- package/dist/api/kws/api.js +44 -26
- package/dist/api/kws/api.js.map +1 -1
- package/dist/api/kws/index.d.ts.map +1 -1
- package/dist/api/kws/index.js +3 -1
- package/dist/api/kws/index.js.map +1 -1
- package/dist/api/kws/webhook.d.ts +3 -1
- package/dist/api/kws/webhook.d.ts.map +1 -1
- package/dist/api/kws/webhook.js +48 -20
- package/dist/api/kws/webhook.js.map +1 -1
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -2
- package/dist/config.js.map +1 -1
- package/dist/data-plane/bsync/index.d.ts.map +1 -1
- package/dist/data-plane/bsync/index.js +22 -0
- package/dist/data-plane/bsync/index.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.js +30 -0
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -1
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/pagination.d.ts +3 -3
- package/dist/data-plane/server/db/tables/actor.d.ts +3 -0
- package/dist/data-plane/server/db/tables/actor.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/actor.js.map +1 -1
- package/dist/data-plane/server/routes/profile.d.ts.map +1 -1
- package/dist/data-plane/server/routes/profile.js +13 -1
- package/dist/data-plane/server/routes/profile.js.map +1 -1
- package/dist/hydration/hydrator.js +1 -1
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/kws.d.ts +35 -0
- package/dist/kws.d.ts.map +1 -1
- package/dist/kws.js +54 -0
- package/dist/kws.js.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +8 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +20 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/stash.d.ts +1 -0
- package/dist/stash.d.ts.map +1 -1
- package/dist/stash.js +1 -0
- package/dist/stash.js.map +1 -1
- package/dist/util/uris.d.ts +2 -2
- package/dist/util/uris.d.ts.map +1 -1
- package/package.json +10 -9
- package/proto/bsky.proto +1 -0
- package/src/api/age-assurance/const.ts +142 -0
- package/src/api/age-assurance/index.ts +34 -0
- package/src/api/age-assurance/kws/age-verified.ts +75 -0
- package/src/api/age-assurance/kws/const.ts +33 -0
- package/src/api/age-assurance/kws/external-payload.test.ts +72 -0
- package/src/api/age-assurance/kws/external-payload.ts +149 -0
- package/src/api/age-assurance/redirects/kws-age-verified.ts +107 -0
- package/src/api/age-assurance/stash.ts +22 -0
- package/src/api/age-assurance/types.ts +10 -0
- package/src/api/age-assurance/util.ts +66 -0
- package/src/api/age-assurance/webhooks/kws-age-verified.ts +75 -0
- package/src/api/app/bsky/ageassurance/begin.ts +167 -0
- package/src/api/app/bsky/ageassurance/getConfig.ts +15 -0
- package/src/api/app/bsky/ageassurance/getState.ts +53 -0
- package/src/api/external.ts +2 -0
- package/src/api/index.ts +6 -0
- package/src/api/kws/api.ts +55 -34
- package/src/api/kws/index.ts +7 -1
- package/src/api/kws/webhook.ts +57 -34
- package/src/config.ts +26 -2
- package/src/data-plane/bsync/index.ts +31 -0
- package/src/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.ts +28 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/actor.ts +3 -0
- package/src/data-plane/server/routes/profile.ts +12 -1
- package/src/hydration/hydrator.ts +1 -1
- package/src/kws.ts +81 -0
- package/src/logger.ts +2 -0
- package/src/proto/bsky_pb.ts +12 -0
- package/src/stash.ts +3 -0
- package/tests/views/age-assurance-v2.test.ts +745 -0
- package/tests/views/age-assurance.test.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
package/dist/stash.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare const Namespaces: {
|
|
|
5
5
|
AppBskyNotificationDefsPreferences: string;
|
|
6
6
|
AppBskyNotificationDefsSubjectActivitySubscription: string;
|
|
7
7
|
AppBskyUnspeccedDefsAgeAssuranceEvent: string;
|
|
8
|
+
AppBskyAgeassuranceDefsEvent: string;
|
|
8
9
|
};
|
|
9
10
|
export type Namespace = (typeof Namespaces)[keyof typeof Namespaces];
|
|
10
11
|
export declare const createStashClient: (bsyncClient: BsyncClient) => StashClient;
|
package/dist/stash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stash.d.ts","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAgB,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"stash.d.ts","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAgB,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAarC,eAAO,MAAM,UAAU;;;;;;CAWtB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAA;AAEpE,eAAO,MAAM,iBAAiB,GAAI,aAAa,WAAW,KAAG,WAE5D,CAAA;AAID,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,WAAW;IAErD,MAAM,CAAC,KAAK,EAAE,WAAW;IAKzB,MAAM,CAAC,KAAK,EAAE,WAAW;IAKzB,MAAM,CAAC,KAAK,EAAE,WAAW;IAIzB,OAAO,CAAC,eAAe;YAOT,YAAY;CAiB3B;AASD,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,SAAS,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,QAAQ,CAAA;CAClB,CAAA;AAED,KAAK,WAAW,GAAG,WAAW,CAAA;AAE9B,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,SAAS,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA"}
|
package/dist/stash.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.Namespaces = {
|
|
|
10
10
|
AppBskyNotificationDefsPreferences: 'app.bsky.notification.defs#preferences',
|
|
11
11
|
AppBskyNotificationDefsSubjectActivitySubscription: 'app.bsky.notification.defs#subjectActivitySubscription',
|
|
12
12
|
AppBskyUnspeccedDefsAgeAssuranceEvent: 'app.bsky.unspecced.defs#ageAssuranceEvent',
|
|
13
|
+
AppBskyAgeassuranceDefsEvent: 'app.bsky.ageassurance.defs#event',
|
|
13
14
|
};
|
|
14
15
|
const createStashClient = (bsyncClient) => {
|
|
15
16
|
return new StashClient(bsyncClient);
|
package/dist/stash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stash.js","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":";AAAA,yCAAyC;;;AAEzC,8CAAyD;AAEzD,iDAA6C;
|
|
1
|
+
{"version":3,"file":"stash.js","sourceRoot":"","sources":["../src/stash.ts"],"names":[],"mappings":";AAAA,yCAAyC;;;AAEzC,8CAAyD;AAEzD,iDAA6C;AAQ7C,+CAAyC;AAI5B,QAAA,UAAU,GAAG;IACxB,2BAA2B,EACzB,iCAA8D;IAChE,kCAAkC,EAChC,wCAAwE;IAC1E,kDAAkD,EAChD,wDAAwG;IAC1G,qCAAqC,EACnC,2CAAiF;IACnF,4BAA4B,EAC1B,kCAA0E;CAC7E,CAAA;AAIM,MAAM,iBAAiB,GAAG,CAAC,WAAwB,EAAe,EAAE;IACzE,OAAO,IAAI,WAAW,CAAC,WAAW,CAAC,CAAA;AACrC,CAAC,CAAA;AAFY,QAAA,iBAAiB,qBAE7B;AAED,8EAA8E;AAC9E,2FAA2F;AAC3F,MAAa,WAAW;IACtB,YAA6B,WAAwB;QAAzC;;;;mBAAiB,WAAW;WAAa;IAAG,CAAC;IAEzD,MAAM,CAAC,KAAkB;QACvB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QACpD,OAAO,IAAI,CAAC,YAAY,CAAC,iBAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,CAAC,KAAkB;QACvB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QACpD,OAAO,IAAI,CAAC,YAAY,CAAC,iBAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAChD,CAAC;IAED,MAAM,CAAC,KAAkB;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,iBAAM,CAAC,MAAM,EAAE,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;IAC3E,CAAC;IAEO,eAAe,CAAC,SAAoB,EAAE,OAAiB;QAC7D,MAAM,MAAM,GAAG,mBAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACpD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,MAAM,CAAC,KAAK,CAAA;QACpB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,KAAwB;QACjE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,KAAK,CAAA;QACnD,MAAM,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC;YAClC,QAAQ;YACR,SAAS;YACT,GAAG;YACH,MAAM;YACN,OAAO,EAAE,OAAO;gBACd,CAAC,CAAC,MAAM,CAAC,IAAI,CACT,IAAA,sBAAY,EAAC;oBACX,KAAK,EAAE,SAAS;oBAChB,GAAG,OAAO;iBACX,CAAC,CACH;gBACH,CAAC,CAAC,SAAS;SACd,CAAC,CAAA;IACJ,CAAC;CACF;AAzCD,kCAyCC","sourcesContent":["/* eslint-disable import/no-deprecated */\n\nimport { LexValue, stringifyLex } from '@atproto/lexicon'\nimport { BsyncClient } from './bsync'\nimport { lexicons } from './lexicon/lexicons'\nimport { Event as AgeAssuranceEventV2 } from './lexicon/types/app/bsky/ageassurance/defs'\nimport { Bookmark } from './lexicon/types/app/bsky/bookmark/defs'\nimport {\n Preferences,\n SubjectActivitySubscription,\n} from './lexicon/types/app/bsky/notification/defs'\nimport { AgeAssuranceEvent } from './lexicon/types/app/bsky/unspecced/defs'\nimport { Method } from './proto/bsync_pb'\n\ntype PickNSID<T extends { $type?: string }> = Exclude<T['$type'], undefined>\n\nexport const Namespaces = {\n AppBskyBookmarkDefsBookmark:\n 'app.bsky.bookmark.defs#bookmark' satisfies PickNSID<Bookmark>,\n AppBskyNotificationDefsPreferences:\n 'app.bsky.notification.defs#preferences' satisfies PickNSID<Preferences>,\n AppBskyNotificationDefsSubjectActivitySubscription:\n 'app.bsky.notification.defs#subjectActivitySubscription' satisfies PickNSID<SubjectActivitySubscription>,\n AppBskyUnspeccedDefsAgeAssuranceEvent:\n 'app.bsky.unspecced.defs#ageAssuranceEvent' satisfies PickNSID<AgeAssuranceEvent>,\n AppBskyAgeassuranceDefsEvent:\n 'app.bsky.ageassurance.defs#event' satisfies PickNSID<AgeAssuranceEventV2>,\n}\n\nexport type Namespace = (typeof Namespaces)[keyof typeof Namespaces]\n\nexport const createStashClient = (bsyncClient: BsyncClient): StashClient => {\n return new StashClient(bsyncClient)\n}\n\n// An abstraction over the BsyncClient, that uses the bsync `PutOperation` RPC\n// to store private data, which can be indexed by the dataplane and queried by the appview.\nexport class StashClient {\n constructor(private readonly bsyncClient: BsyncClient) {}\n\n create(input: CreateInput) {\n this.validateLexicon(input.namespace, input.payload)\n return this.putOperation(Method.CREATE, input)\n }\n\n update(input: UpdateInput) {\n this.validateLexicon(input.namespace, input.payload)\n return this.putOperation(Method.UPDATE, input)\n }\n\n delete(input: DeleteInput) {\n return this.putOperation(Method.DELETE, { ...input, payload: undefined })\n }\n\n private validateLexicon(namespace: Namespace, payload: LexValue) {\n const result = lexicons.validate(namespace, payload)\n if (!result.success) {\n throw result.error\n }\n }\n\n private async putOperation(method: Method, input: PutOperationInput) {\n const { actorDid, namespace, key, payload } = input\n await this.bsyncClient.putOperation({\n actorDid,\n namespace,\n key,\n method,\n payload: payload\n ? Buffer.from(\n stringifyLex({\n $type: namespace,\n ...payload,\n }),\n )\n : undefined,\n })\n }\n}\n\ntype PutOperationInput = {\n actorDid: string\n namespace: Namespace\n key: string\n payload: LexValue | undefined\n}\n\ntype CreateInput = {\n actorDid: string\n namespace: Namespace\n key: string\n payload: LexValue\n}\n\ntype UpdateInput = CreateInput\n\ntype DeleteInput = {\n actorDid: string\n namespace: Namespace\n key: string\n}\n"]}
|
package/dist/util/uris.d.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { Main as StrongRef } from '../lexicon/types/com/atproto/repo/strongRef';
|
|
|
3
3
|
* Convert a post URI to a threadgate URI. If the URI is not a valid
|
|
4
4
|
* post URI, return URI unchanged. Threadgate lookups will then fail.
|
|
5
5
|
*/
|
|
6
|
-
export declare function postUriToThreadgateUri(postUri: string):
|
|
6
|
+
export declare function postUriToThreadgateUri(postUri: string): import("@atproto/syntax").AtUriString;
|
|
7
7
|
/**
|
|
8
8
|
* Convert a post URI to a postgate URI. If the URI is not a valid
|
|
9
9
|
* post URI, return URI unchanged. Postgate lookups will then fail.
|
|
10
10
|
*/
|
|
11
|
-
export declare function postUriToPostgateUri(postUri: string):
|
|
11
|
+
export declare function postUriToPostgateUri(postUri: string): import("@atproto/syntax").AtUriString;
|
|
12
12
|
export declare function uriToDid(uri: string): string;
|
|
13
13
|
export declare function safePinnedPost(value: unknown): StrongRef | undefined;
|
|
14
14
|
//# sourceMappingURL=uris.d.ts.map
|
package/dist/util/uris.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uris.d.ts","sourceRoot":"","sources":["../../src/util/uris.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,IAAI,IAAI,SAAS,EAElB,MAAM,6CAA6C,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"uris.d.ts","sourceRoot":"","sources":["../../src/util/uris.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,IAAI,IAAI,SAAS,EAElB,MAAM,6CAA6C,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,yCAMrD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,yCAMnD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,UAEnC;AAGD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,yBAS5C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.199",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -53,22 +53,23 @@
|
|
|
53
53
|
"zod": "3.23.8",
|
|
54
54
|
"@atproto-labs/fetch-node": "0.2.0",
|
|
55
55
|
"@atproto-labs/xrpc-utils": "0.0.24",
|
|
56
|
-
"@atproto/api": "^0.18.
|
|
57
|
-
"@atproto/common": "^0.5.
|
|
58
|
-
"@atproto/crypto": "^0.4.
|
|
59
|
-
"@atproto/did": "^0.2.
|
|
56
|
+
"@atproto/api": "^0.18.4",
|
|
57
|
+
"@atproto/common": "^0.5.2",
|
|
58
|
+
"@atproto/crypto": "^0.4.5",
|
|
59
|
+
"@atproto/did": "^0.2.3",
|
|
60
60
|
"@atproto/identity": "^0.4.10",
|
|
61
61
|
"@atproto/lexicon": "^0.5.2",
|
|
62
62
|
"@atproto/repo": "^0.8.11",
|
|
63
63
|
"@atproto/sync": "^0.1.38",
|
|
64
|
-
"@atproto/syntax": "^0.4.
|
|
65
|
-
"@atproto/xrpc-server": "^0.10.
|
|
64
|
+
"@atproto/syntax": "^0.4.2",
|
|
65
|
+
"@atproto/xrpc-server": "^0.10.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@bufbuild/buf": "^1.28.1",
|
|
69
69
|
"@bufbuild/protoc-gen-es": "^1.5.0",
|
|
70
70
|
"@connectrpc/protoc-gen-connect-es": "^1.1.4",
|
|
71
71
|
"@did-plc/server": "^0.0.1",
|
|
72
|
+
"@jest/globals": "28",
|
|
72
73
|
"@types/cors": "^2.8.12",
|
|
73
74
|
"@types/express": "^4.17.13",
|
|
74
75
|
"@types/express-serve-static-core": "^4.17.36",
|
|
@@ -77,9 +78,9 @@
|
|
|
77
78
|
"jest": "^28.1.2",
|
|
78
79
|
"ts-node": "^10.8.2",
|
|
79
80
|
"typescript": "^5.6.3",
|
|
80
|
-
"@atproto/api": "^0.18.
|
|
81
|
+
"@atproto/api": "^0.18.4",
|
|
81
82
|
"@atproto/lex-cli": "^0.9.7",
|
|
82
|
-
"@atproto/pds": "^0.4.
|
|
83
|
+
"@atproto/pds": "^0.4.197",
|
|
83
84
|
"@atproto/xrpc": "^0.7.6"
|
|
84
85
|
},
|
|
85
86
|
"scripts": {
|
package/proto/bsky.proto
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppBskyAgeassuranceDefs,
|
|
3
|
+
ageAssuranceRuleIDs as ids,
|
|
4
|
+
} from '@atproto/api'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Age assurance configuration defining rules for various regions.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: These rules are matched in order, and the first matching rule
|
|
10
|
+
* determines the access level granted.
|
|
11
|
+
*
|
|
12
|
+
* NOTE: all regions MUST have a default rule as the last rule.
|
|
13
|
+
*/
|
|
14
|
+
export const AGE_ASSURANCE_CONFIG: AppBskyAgeassuranceDefs.Config = {
|
|
15
|
+
regions: [
|
|
16
|
+
{
|
|
17
|
+
countryCode: 'GB',
|
|
18
|
+
regionCode: undefined,
|
|
19
|
+
rules: [
|
|
20
|
+
{
|
|
21
|
+
$type: ids.IfAssuredOverAge,
|
|
22
|
+
age: 18,
|
|
23
|
+
access: 'full',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
$type: ids.IfDeclaredOverAge,
|
|
27
|
+
age: 13,
|
|
28
|
+
access: 'safe',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
$type: ids.Default,
|
|
32
|
+
access: 'none',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
countryCode: 'AU',
|
|
38
|
+
regionCode: undefined,
|
|
39
|
+
rules: [
|
|
40
|
+
{
|
|
41
|
+
$type: ids.IfAccountNewerThan,
|
|
42
|
+
date: '2025-12-10T00:00:00Z',
|
|
43
|
+
access: 'none',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
$type: ids.IfAssuredOverAge,
|
|
47
|
+
age: 18,
|
|
48
|
+
access: 'full',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
$type: ids.IfAssuredOverAge,
|
|
52
|
+
age: 16,
|
|
53
|
+
access: 'safe',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
$type: ids.IfDeclaredOverAge,
|
|
57
|
+
age: 16,
|
|
58
|
+
access: 'safe',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
$type: ids.Default,
|
|
62
|
+
access: 'none',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
countryCode: 'US',
|
|
68
|
+
regionCode: 'SD',
|
|
69
|
+
rules: [
|
|
70
|
+
{
|
|
71
|
+
$type: ids.IfAssuredOverAge,
|
|
72
|
+
age: 18,
|
|
73
|
+
access: 'full',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
$type: ids.IfDeclaredOverAge,
|
|
77
|
+
age: 13,
|
|
78
|
+
access: 'safe',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
$type: ids.Default,
|
|
82
|
+
access: 'none',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
countryCode: 'US',
|
|
88
|
+
regionCode: 'WY',
|
|
89
|
+
rules: [
|
|
90
|
+
{
|
|
91
|
+
$type: ids.IfAssuredOverAge,
|
|
92
|
+
age: 18,
|
|
93
|
+
access: 'full',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
$type: ids.IfDeclaredOverAge,
|
|
97
|
+
age: 13,
|
|
98
|
+
access: 'safe',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
$type: ids.Default,
|
|
102
|
+
access: 'none',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
countryCode: 'US',
|
|
108
|
+
regionCode: 'OH',
|
|
109
|
+
rules: [
|
|
110
|
+
{
|
|
111
|
+
$type: ids.IfAssuredOverAge,
|
|
112
|
+
age: 18,
|
|
113
|
+
access: 'full',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
$type: ids.IfDeclaredOverAge,
|
|
117
|
+
age: 13,
|
|
118
|
+
access: 'safe',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
$type: ids.Default,
|
|
122
|
+
access: 'none',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
countryCode: 'US',
|
|
128
|
+
regionCode: 'MS',
|
|
129
|
+
rules: [
|
|
130
|
+
{
|
|
131
|
+
$type: ids.IfAssuredOverAge,
|
|
132
|
+
age: 18,
|
|
133
|
+
access: 'full',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
$type: ids.Default,
|
|
137
|
+
access: 'none',
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Router, raw } from 'express'
|
|
2
|
+
import { AppContext } from '../../context'
|
|
3
|
+
import { webhookAuth } from '../kws/webhook'
|
|
4
|
+
import { handler as ageVerifiedRedirect } from './redirects/kws-age-verified'
|
|
5
|
+
import { AppContextWithAA } from './types'
|
|
6
|
+
import { handler as ageVerifiedWebhook } from './webhooks/kws-age-verified'
|
|
7
|
+
|
|
8
|
+
export const createRouter = (ctx: AppContext): Router => {
|
|
9
|
+
assertAppContextWithAgeAssuranceClient(ctx)
|
|
10
|
+
|
|
11
|
+
const router = Router()
|
|
12
|
+
router.use(raw({ type: 'application/json' }))
|
|
13
|
+
router.post(
|
|
14
|
+
'/age-assurance/webhooks/kws-age-verified',
|
|
15
|
+
webhookAuth({
|
|
16
|
+
secret: ctx.cfg.kws.ageVerifiedWebhookSecret,
|
|
17
|
+
}),
|
|
18
|
+
ageVerifiedWebhook(ctx),
|
|
19
|
+
)
|
|
20
|
+
router.get(
|
|
21
|
+
'/age-assurance/redirects/kws-age-verified',
|
|
22
|
+
ageVerifiedRedirect(ctx),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
return router
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const assertAppContextWithAgeAssuranceClient: (
|
|
29
|
+
ctx: AppContext,
|
|
30
|
+
) => asserts ctx is AppContextWithAA = (ctx: AppContext) => {
|
|
31
|
+
if (!ctx.kwsClient) {
|
|
32
|
+
throw new Error('Tried to set up KWS router without kwsClient configured.')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Schema for KWS the `status` object on `age-verified` payloads.
|
|
5
|
+
*/
|
|
6
|
+
export const KWSAgeVerifiedStatusSchema = z.object({
|
|
7
|
+
verified: z.boolean(),
|
|
8
|
+
verifiedMinimumAge: z.number(),
|
|
9
|
+
transactionId: z.string().optional(),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The KWS `status` object on `age-verified` payloads.
|
|
14
|
+
*/
|
|
15
|
+
export type KWSAgeVerifiedStatus = z.infer<typeof KWSAgeVerifiedStatusSchema>
|
|
16
|
+
|
|
17
|
+
export function serializeKWSAgeVerifiedStatus(
|
|
18
|
+
status: KWSAgeVerifiedStatus,
|
|
19
|
+
): string {
|
|
20
|
+
return JSON.stringify(KWSAgeVerifiedStatusSchema.parse(status))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse KWS `age-verified` status object.
|
|
25
|
+
*/
|
|
26
|
+
export const parseKWSAgeVerifiedStatus = (
|
|
27
|
+
raw: string,
|
|
28
|
+
): KWSAgeVerifiedStatus => {
|
|
29
|
+
try {
|
|
30
|
+
const value = JSON.parse(raw)
|
|
31
|
+
return KWSAgeVerifiedStatusSchema.parse(value)
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw new Error(`Invalid KWS age-verified status: ${raw}`, {
|
|
34
|
+
cause: err,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema for KWS `age-verified` webhooks.
|
|
41
|
+
*
|
|
42
|
+
* Note: we don't use `.strict()` here so that we avoid breaking if KWS adds
|
|
43
|
+
* fields, and some fields below are not strictly typed since we're not using
|
|
44
|
+
* them.
|
|
45
|
+
*/
|
|
46
|
+
export const KWSAgeVerifiedWebhookSchema = z.object({
|
|
47
|
+
name: z.string(),
|
|
48
|
+
time: z.string(), // ISO8601 timestamp, but don't validate here
|
|
49
|
+
orgId: z.string().uuid().optional(),
|
|
50
|
+
productId: z.string().uuid().optional(),
|
|
51
|
+
payload: z.object({
|
|
52
|
+
email: z.string(), // no need to validate here
|
|
53
|
+
externalPayload: z.string(),
|
|
54
|
+
status: KWSAgeVerifiedStatusSchema,
|
|
55
|
+
}),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The raw KWS `age-verified` webhook body
|
|
60
|
+
*/
|
|
61
|
+
export type KWSWebhookAgeVerified = z.infer<typeof KWSAgeVerifiedWebhookSchema>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parse KWS `age-verified` webhook body and its external payload.
|
|
65
|
+
*/
|
|
66
|
+
export const parseKWSAgeVerifiedWebhook = (
|
|
67
|
+
raw: string,
|
|
68
|
+
): KWSWebhookAgeVerified => {
|
|
69
|
+
try {
|
|
70
|
+
const value: unknown = JSON.parse(raw)
|
|
71
|
+
return KWSAgeVerifiedWebhookSchema.parse(value)
|
|
72
|
+
} catch (err) {
|
|
73
|
+
throw new Error(`Invalid webhook body: ${raw}`, { cause: err })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported languages for KWS Adult Verification. This list comes from KWS's
|
|
3
|
+
* Age Verification Developer Guide PDF doc.
|
|
4
|
+
*/
|
|
5
|
+
export const KWS_SUPPORTED_LANGUAGES = new Set([
|
|
6
|
+
'en',
|
|
7
|
+
'ar',
|
|
8
|
+
'zh-Hans',
|
|
9
|
+
'nl',
|
|
10
|
+
'tl',
|
|
11
|
+
'fr',
|
|
12
|
+
'de',
|
|
13
|
+
'id',
|
|
14
|
+
'it',
|
|
15
|
+
'ja',
|
|
16
|
+
'ko',
|
|
17
|
+
'pl',
|
|
18
|
+
'pt-BR',
|
|
19
|
+
'pt',
|
|
20
|
+
'ru',
|
|
21
|
+
'es',
|
|
22
|
+
'th',
|
|
23
|
+
'tr',
|
|
24
|
+
'vi',
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Regions where our "version 2" using the `age-verified` KWS flow is
|
|
29
|
+
* available. In these regions, we'll use a different KWS flow from the
|
|
30
|
+
* existing `adult-verified` flow, pass along a different external payload, and
|
|
31
|
+
* handle webhooks/redirects differently in the appview.
|
|
32
|
+
*/
|
|
33
|
+
export const KWS_V2_COUNTRIES = new Set(['AU'])
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals'
|
|
2
|
+
import {
|
|
3
|
+
KWSExternalPayloadVersion,
|
|
4
|
+
parseKWSExternalPayloadV1WithV2Compat,
|
|
5
|
+
parseKWSExternalPayloadV2,
|
|
6
|
+
parseKWSExternalPayloadVersion,
|
|
7
|
+
serializeKWSExternalPayloadV1,
|
|
8
|
+
serializeKWSExternalPayloadV2,
|
|
9
|
+
} from './external-payload'
|
|
10
|
+
|
|
11
|
+
describe('parseKWSExternalPayloadVersion', () => {
|
|
12
|
+
it('should return V2 for "2"', () => {
|
|
13
|
+
const result = parseKWSExternalPayloadVersion('2')
|
|
14
|
+
expect(result).toBe('2')
|
|
15
|
+
})
|
|
16
|
+
it('should return V1 for unknown versions', () => {
|
|
17
|
+
const result = parseKWSExternalPayloadVersion('unknown')
|
|
18
|
+
expect(result).toBe('1')
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('parseKWSExternalPayloadV1WithV2Compat', () => {
|
|
23
|
+
it('should parse V1 payload correctly', () => {
|
|
24
|
+
const payload = {
|
|
25
|
+
attemptId: '123',
|
|
26
|
+
actorDid: 'did:plc:123',
|
|
27
|
+
}
|
|
28
|
+
const serialized = serializeKWSExternalPayloadV1(payload)
|
|
29
|
+
const result = parseKWSExternalPayloadV1WithV2Compat(serialized)
|
|
30
|
+
expect(result).toEqual({
|
|
31
|
+
version: KWSExternalPayloadVersion.V1,
|
|
32
|
+
...payload,
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
it('should parse V2 payload correctly', () => {
|
|
36
|
+
const payload = {
|
|
37
|
+
version: KWSExternalPayloadVersion.V2 as const,
|
|
38
|
+
attemptId: '123',
|
|
39
|
+
actorDid: 'did:plc:123',
|
|
40
|
+
countryCode: 'US',
|
|
41
|
+
}
|
|
42
|
+
const serialized = serializeKWSExternalPayloadV2(payload)
|
|
43
|
+
const result = parseKWSExternalPayloadV1WithV2Compat(serialized)
|
|
44
|
+
expect(result).toEqual(payload)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('serializeKWSExternalPayloadV2 & parseKWSExternalPayloadV2', () => {
|
|
49
|
+
const payload = {
|
|
50
|
+
version: KWSExternalPayloadVersion.V2 as const,
|
|
51
|
+
attemptId: '123',
|
|
52
|
+
actorDid: 'did:plc:123',
|
|
53
|
+
countryCode: 'US',
|
|
54
|
+
regionCode: 'CA',
|
|
55
|
+
}
|
|
56
|
+
it('compresses when serializing', () => {
|
|
57
|
+
const serialized = serializeKWSExternalPayloadV2(payload)
|
|
58
|
+
const comparison = JSON.stringify({
|
|
59
|
+
v: KWSExternalPayloadVersion.V2,
|
|
60
|
+
id: payload.attemptId,
|
|
61
|
+
did: payload.actorDid,
|
|
62
|
+
gc: payload.countryCode,
|
|
63
|
+
gr: payload.regionCode,
|
|
64
|
+
})
|
|
65
|
+
expect(serialized).toEqual(comparison)
|
|
66
|
+
})
|
|
67
|
+
it('decompresses when parsing', () => {
|
|
68
|
+
const serialized = serializeKWSExternalPayloadV2(payload)
|
|
69
|
+
const deserialized = parseKWSExternalPayloadV2(serialized)
|
|
70
|
+
expect(deserialized).toEqual(payload)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT = 250
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when the provided external payload exceeds KWS's character limit.
|
|
7
|
+
*
|
|
8
|
+
* This is most commonly caused by DIDs that are too long, such as for
|
|
9
|
+
* `did:web` DIDs. But it's very rare, and the client has special handling for
|
|
10
|
+
* this case.
|
|
11
|
+
*/
|
|
12
|
+
export class KWSExternalPayloadTooLargeError extends Error {}
|
|
13
|
+
|
|
14
|
+
export enum KWSExternalPayloadVersion {
|
|
15
|
+
V1 = '1',
|
|
16
|
+
V2 = '2',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function parseKWSExternalPayloadVersion(raw: string) {
|
|
20
|
+
switch (raw) {
|
|
21
|
+
case KWSExternalPayloadVersion.V2:
|
|
22
|
+
return KWSExternalPayloadVersion.V2
|
|
23
|
+
default:
|
|
24
|
+
return KWSExternalPayloadVersion.V1
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type KWSExternalPayloadV1 = {
|
|
29
|
+
actorDid: string
|
|
30
|
+
attemptId: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const KWSExternalPayloadV1Schema = z.object({
|
|
34
|
+
actorDid: z.string(),
|
|
35
|
+
attemptId: z.string(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export function parseKWSExternalPayloadV1(raw: string): KWSExternalPayloadV1 {
|
|
39
|
+
try {
|
|
40
|
+
const value: unknown = JSON.parse(raw)
|
|
41
|
+
return KWSExternalPayloadV1Schema.parse(value)
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw new Error(`Failed to parse KWSExternalPayloadV1`, {
|
|
44
|
+
cause: err,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function serializeKWSExternalPayloadV1(
|
|
50
|
+
payload: KWSExternalPayloadV1,
|
|
51
|
+
): string {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.stringify(KWSExternalPayloadV1Schema.parse(payload))
|
|
54
|
+
} catch (err) {
|
|
55
|
+
throw new Error('Failed to serialize KWSExternalPayloadV1', { cause: err })
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* During our migration from v1 to v2 of the KWS external payload, we'll be
|
|
61
|
+
* sending v2 payloads on the v1 flow (the `adult-verified` email flow). We use
|
|
62
|
+
* this utility to parse either v1 or v2 payloads in that flow.
|
|
63
|
+
*
|
|
64
|
+
* Check for the `version` field on the output of this method to discriminate
|
|
65
|
+
* between the two types and handle them differently.
|
|
66
|
+
*/
|
|
67
|
+
export function parseKWSExternalPayloadV1WithV2Compat(
|
|
68
|
+
raw: string,
|
|
69
|
+
):
|
|
70
|
+
| (KWSExternalPayloadV1 & { version: KWSExternalPayloadVersion.V1 })
|
|
71
|
+
| KWSExternalPayloadV2 {
|
|
72
|
+
const deserialized = JSON.parse(raw)
|
|
73
|
+
const v2 = deserialized.v === KWSExternalPayloadVersion.V2
|
|
74
|
+
|
|
75
|
+
if (v2) {
|
|
76
|
+
return parseKWSExternalPayloadV2(raw)
|
|
77
|
+
} else {
|
|
78
|
+
return {
|
|
79
|
+
...parseKWSExternalPayloadV1(raw),
|
|
80
|
+
version: KWSExternalPayloadVersion.V1,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/***************************
|
|
86
|
+
* KWS External Payload V2 *
|
|
87
|
+
***************************/
|
|
88
|
+
|
|
89
|
+
export type KWSExternalPayloadV2 = {
|
|
90
|
+
version: KWSExternalPayloadVersion.V2
|
|
91
|
+
attemptId: string
|
|
92
|
+
actorDid: string
|
|
93
|
+
countryCode: string
|
|
94
|
+
regionCode?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const KWSExternalPayloadV2Schema = z.object({
|
|
98
|
+
v: z.string(),
|
|
99
|
+
id: z.string(),
|
|
100
|
+
did: z.string(),
|
|
101
|
+
gc: z.string().length(2),
|
|
102
|
+
gr: z.string().optional(),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
export function serializeKWSExternalPayloadV2(
|
|
106
|
+
payload: KWSExternalPayloadV2,
|
|
107
|
+
): string {
|
|
108
|
+
let compressed: z.infer<typeof KWSExternalPayloadV2Schema>
|
|
109
|
+
try {
|
|
110
|
+
compressed = KWSExternalPayloadV2Schema.parse({
|
|
111
|
+
v: KWSExternalPayloadVersion.V2, // version
|
|
112
|
+
id: payload.attemptId,
|
|
113
|
+
did: payload.actorDid,
|
|
114
|
+
gc: payload.countryCode, // geolocation country
|
|
115
|
+
gr: payload.regionCode, // geolocation region
|
|
116
|
+
})
|
|
117
|
+
} catch (err) {
|
|
118
|
+
throw new Error('Failed to serialize KWSExternalPayloadV2', { cause: err })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const serialized = JSON.stringify(compressed)
|
|
122
|
+
|
|
123
|
+
if (serialized.length > KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT) {
|
|
124
|
+
throw new KWSExternalPayloadTooLargeError(
|
|
125
|
+
`Serialized external payload size ${serialized.length} exceeds limit of ${KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT}`,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return serialized
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function parseKWSExternalPayloadV2(raw: string): KWSExternalPayloadV2 {
|
|
133
|
+
try {
|
|
134
|
+
const deserialized = JSON.parse(raw)
|
|
135
|
+
const parsed = KWSExternalPayloadV2Schema.parse(deserialized)
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
version: KWSExternalPayloadVersion.V2,
|
|
139
|
+
attemptId: parsed.id,
|
|
140
|
+
actorDid: parsed.did,
|
|
141
|
+
countryCode: parsed.gc,
|
|
142
|
+
regionCode: parsed.gr,
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
throw new Error(`Failed to parse KWSExternalPayloadV2`, {
|
|
146
|
+
cause: err,
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|