@chaindaddy/js 0.3.0 → 0.3.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/dist/api.d.ts +177 -20
- package/dist/cd.js +3 -3
- package/dist/cd.js.sri +1 -1
- package/dist/core.d.ts +3 -2
- package/dist/index.js +4 -4
- package/dist/signin.js +3 -3
- package/package.json +2 -1
package/dist/api.d.ts
CHANGED
|
@@ -15,8 +15,8 @@ export interface paths {
|
|
|
15
15
|
* @description A verified coin page by chain and ticker: its name, token address, registration ID, logo, price,
|
|
16
16
|
* market cap, 24-hour volume, liquidity, 24-hour price change and holder count. A figure Chain Daddy
|
|
17
17
|
* does not have is `null`, never `0`. The logo is `null` for a page marked NSFW. Answers 404 for a
|
|
18
|
-
* ticker with no verified page on that chain
|
|
19
|
-
* about a minute.
|
|
18
|
+
* ticker with no verified page on that chain, and for a page Chain Daddy removed. Keyless, the same
|
|
19
|
+
* for every caller, and cached for about a minute.
|
|
20
20
|
*/
|
|
21
21
|
get: operations["getPublicCoin"];
|
|
22
22
|
put?: never;
|
|
@@ -38,9 +38,10 @@ export interface paths {
|
|
|
38
38
|
* List the apps on a verified coin's page
|
|
39
39
|
* @description The apps a verified coin's page shows, in page order: each app's ID, name, description, version,
|
|
40
40
|
* category, icon and whether it is a built-in. It names the apps; it does not include what the owner
|
|
41
|
-
* set up inside them. Apps turned off are left out, and so is every app rated NSFW
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* set up inside them. Apps turned off are left out, and so is every app rated NSFW and every app the
|
|
42
|
+
* page itself no longer shows (not reviewed yet, suspended or removed). Answers 404 for a ticker
|
|
43
|
+
* with no verified page on that chain, and for a page Chain Daddy removed. Keyless, the same for
|
|
44
|
+
* every caller, and cached for about a minute.
|
|
44
45
|
*/
|
|
45
46
|
get: operations["listPublicCoinApps"];
|
|
46
47
|
put?: never;
|
|
@@ -64,7 +65,8 @@ export interface paths {
|
|
|
64
65
|
* smallest unit as a decimal string, and `holds` (whether it is above zero). Use it to re-check a
|
|
65
66
|
* holding after someone signed in to your site with Chain Daddy, without an API key. Works for any
|
|
66
67
|
* token on a chain Chain Daddy serves. Each caller gets 60 requests a minute; past that it
|
|
67
|
-
* answers 429. Cached for about 30 seconds.
|
|
68
|
+
* answers 429. Cached for about 30 seconds. All callers together share a limit on chain reads:
|
|
69
|
+
* in a busy moment it answers 503 with `Retry-After`.
|
|
68
70
|
*/
|
|
69
71
|
get: operations["getPublicHolding"];
|
|
70
72
|
put?: never;
|
|
@@ -86,10 +88,11 @@ export interface paths {
|
|
|
86
88
|
* List a registration's verified sites
|
|
87
89
|
* @description The websites a coin's registration has verified with a DNS record. `badgeHosts` show the coin's
|
|
88
90
|
* verified badge on the Chain Daddy sign-in page. `embedHosts` may also run the coin's apps and take
|
|
89
|
-
* its checkout. A host covers its subdomains. `interactive` is `false` when there are no
|
|
91
|
+
* its checkout. A host covers its subdomains, except one Chain Daddy has blocked. `interactive` is `false` when there are no
|
|
90
92
|
* `embedHosts` or Chain Daddy has turned the coin's embeds off. A registration with no verified
|
|
91
|
-
* sites answers 200 with empty lists.
|
|
92
|
-
*
|
|
93
|
+
* sites answers 200 with empty lists. Only the registration a verified coin page shows answers
|
|
94
|
+
* (the `crownId` the coin read gives); any other ID answers 404. Keyless, the same for every
|
|
95
|
+
* caller, and cached for about a minute.
|
|
93
96
|
*/
|
|
94
97
|
get: operations["getPublicCrownSites"];
|
|
95
98
|
put?: never;
|
|
@@ -833,6 +836,12 @@ export interface paths {
|
|
|
833
836
|
* A buyer paying a checkout session sends `checkoutSessionId` from their own session on chaindaddy.io. The
|
|
834
837
|
* session then fixes the store, the SKU, the quantity and `appAccountToken`, so `storeId` and `sku` may be
|
|
835
838
|
* left out. The amount and the fee are quoted exactly as for any other order of that item.
|
|
839
|
+
*
|
|
840
|
+
* When the session's item belongs to an installed app, the order is that app's order: it pays the page, the
|
|
841
|
+
* fee and the app's developer, needs `acknowledgeFinalSale: true`, and stays inside the app's limits. The app
|
|
842
|
+
* is the item's own; an `appId` sent with a session is ignored. Everything is checked again when the order is
|
|
843
|
+
* made, so a session opened while the item could be sold answers 409 `APP_ITEM_NOT_SOLD_HERE` once its
|
|
844
|
+
* developer turns that off, pauses, or changes the item's terms.
|
|
836
845
|
*/
|
|
837
846
|
post: operations["createIapOrder"];
|
|
838
847
|
delete?: never;
|
|
@@ -884,8 +893,12 @@ export interface paths {
|
|
|
884
893
|
*
|
|
885
894
|
* `successUrl` and `cancelUrl` must be `https` pages on one of the coin's verified sites, or a subdomain of
|
|
886
895
|
* one (400 `RETURN_URL_NOT_VERIFIED`, which lists the sites that pass). The wallet that opened the store
|
|
887
|
-
* must have signed the Seller Terms (403 `SELLER_TERMS_REQUIRED`).
|
|
888
|
-
*
|
|
896
|
+
* must have signed the Seller Terms (403 `SELLER_TERMS_REQUIRED`).
|
|
897
|
+
*
|
|
898
|
+
* An item of an app installed on the coin's page (its `sku` starts with `app.`) is sold through a session
|
|
899
|
+
* only when the app's developer has allowed that item to be sold outside the app; otherwise the call answers
|
|
900
|
+
* 409 `APP_ITEM_NOT_SOLD_HERE`, with a `reason`. The sale pays the same wallets as inside the app: the page,
|
|
901
|
+
* the fee and the developer's share. Such a session takes at most the app's per-order quantity.
|
|
889
902
|
*
|
|
890
903
|
* Send an `Idempotency-Key`. A retry of the same request returns the session it opened, with status 200
|
|
891
904
|
* and the header `Idempotent-Replayed: true`.
|
|
@@ -946,6 +959,10 @@ export interface paths {
|
|
|
946
959
|
* needs no credential (the session id in the buyer's link is enough) and never carries the session's
|
|
947
960
|
* `metadata` or `clientReferenceId`. The answer is never cached.
|
|
948
961
|
*
|
|
962
|
+
* When the item belongs to an app installed on the coin's page, `sku` names the app (`appId`) and carries
|
|
963
|
+
* nothing else of the app's: no `appBps`, `appTermsVersion`, `appItemKey` or `appRole`, and an empty
|
|
964
|
+
* `metadata`.
|
|
965
|
+
*
|
|
949
966
|
* Pass `origin` to learn whether a page on that origin may open this checkout in a popup
|
|
950
967
|
* (`openerAllowed`): it must be `https` on one of the coin's verified sites.
|
|
951
968
|
*
|
|
@@ -1019,7 +1036,8 @@ export interface paths {
|
|
|
1019
1036
|
* List the event types a webhook can subscribe to
|
|
1020
1037
|
* @description Returns every subscribable event type with a one-line description: registration, heartbeat, holder, trade
|
|
1021
1038
|
* and alert events, API quota events, deal lifecycle events (`deal.created`, `deal.accepted`,
|
|
1022
|
-
* `deal.rejected`, `deal.cancelled`, `deal.completed`, `deal.failed`)
|
|
1039
|
+
* `deal.rejected`, `deal.cancelled`, `deal.completed`, `deal.failed`), creator-store events (`iap.*`) and
|
|
1040
|
+
* `app.order.confirmed`, which tells a paid app's developer of each confirmed order made through the app.
|
|
1023
1041
|
* This list is exactly what create and update accept. Read it before creating a webhook rather than
|
|
1024
1042
|
* guessing names: a misspelled type is refused with 400.
|
|
1025
1043
|
*
|
|
@@ -1343,6 +1361,18 @@ export interface components {
|
|
|
1343
1361
|
error: string;
|
|
1344
1362
|
/** @description Machine-readable error code (e.g. `IAP_PLAN_REQUIRED`, `INSUFFICIENT_QUANTITY`) */
|
|
1345
1363
|
code?: string;
|
|
1364
|
+
/**
|
|
1365
|
+
* @description Which of a code's causes this is, where a code has several. Sent with `APP_NOT_SELLING`:
|
|
1366
|
+
* `app_not_published`, `selling_switched_off` (stopped by Chain Daddy), `developer_paused`
|
|
1367
|
+
* (paused by the app's developer), `developer_unavailable` (the developer cannot sell right
|
|
1368
|
+
* now; why is between them and Chain Daddy, and their own portal tells them), `no_payout_on_chain`
|
|
1369
|
+
* or `payout_contract_missing` (the developer is not paid on this chain). Sent with
|
|
1370
|
+
* `APP_ITEM_NOT_SOLD_HERE` (an app's item and a checkout session): any of those, or
|
|
1371
|
+
* `not_allowed_outside_app` (the app's developer has not allowed the item to be sold outside the app),
|
|
1372
|
+
* `app_not_installed`, `terms_changed` or `item_not_for_sale`. Absent on every other error. Expect new
|
|
1373
|
+
* values.
|
|
1374
|
+
*/
|
|
1375
|
+
reason?: string;
|
|
1346
1376
|
};
|
|
1347
1377
|
/** @description One token prize. Amounts are base units, as strings. */
|
|
1348
1378
|
IapAirdropAward: {
|
|
@@ -2460,8 +2490,8 @@ export interface components {
|
|
|
2460
2490
|
iat: number;
|
|
2461
2491
|
/** @description Five minutes after `iat` */
|
|
2462
2492
|
exp: number;
|
|
2463
|
-
/** @description When the Chain Daddy session this sign-in rides on began */
|
|
2464
|
-
auth_time
|
|
2493
|
+
/** @description When the Chain Daddy session this sign-in rides on began. Only for a client whose `sub` is the wallet; a client with a per-site `sub` gets none, because the same value at every site would link its ids */
|
|
2494
|
+
auth_time?: number;
|
|
2465
2495
|
/** @description Your `nonce`, when you sent one */
|
|
2466
2496
|
nonce?: string;
|
|
2467
2497
|
jti: string;
|
|
@@ -2740,7 +2770,7 @@ export interface operations {
|
|
|
2740
2770
|
/** @description The registration's ID on that chain */
|
|
2741
2771
|
crownId: number;
|
|
2742
2772
|
tokenAddress: string;
|
|
2743
|
-
/** @description The page's logo; `null` when it has none
|
|
2773
|
+
/** @description The page's logo; `null` when it has none, the page is marked NSFW, or Chain Daddy removed the image */
|
|
2744
2774
|
logoUrl: string | null;
|
|
2745
2775
|
/** @description Whether the page carries the NSFW label */
|
|
2746
2776
|
nsfw: boolean;
|
|
@@ -2758,6 +2788,21 @@ export interface operations {
|
|
|
2758
2788
|
};
|
|
2759
2789
|
};
|
|
2760
2790
|
};
|
|
2791
|
+
/** @description The request came with the user agent of an AI or LLM crawler, and no cached answer was there to serve instead. Never sent to a browser or to a server using its own user agent. */
|
|
2792
|
+
403: {
|
|
2793
|
+
headers: {
|
|
2794
|
+
[name: string]: unknown;
|
|
2795
|
+
};
|
|
2796
|
+
content: {
|
|
2797
|
+
/**
|
|
2798
|
+
* @example {
|
|
2799
|
+
* "error": "ai_bot_blocked",
|
|
2800
|
+
* "message": "AI/LLM scraping is not permitted. Use our API with a developer-tier API key for programmatic access."
|
|
2801
|
+
* }
|
|
2802
|
+
*/
|
|
2803
|
+
"application/json": components["schemas"]["Error"];
|
|
2804
|
+
};
|
|
2805
|
+
};
|
|
2761
2806
|
/** @description No verified page for that ticker on that chain, or an unknown chain */
|
|
2762
2807
|
404: {
|
|
2763
2808
|
headers: {
|
|
@@ -2774,6 +2819,24 @@ export interface operations {
|
|
|
2774
2819
|
"application/json": components["schemas"]["Error"];
|
|
2775
2820
|
};
|
|
2776
2821
|
};
|
|
2822
|
+
/** @description More than 400 requests in a minute from one IP address, across the whole API. Never cached. */
|
|
2823
|
+
429: {
|
|
2824
|
+
headers: {
|
|
2825
|
+
/** @description Seconds to wait before trying again */
|
|
2826
|
+
"Retry-After"?: number;
|
|
2827
|
+
[name: string]: unknown;
|
|
2828
|
+
};
|
|
2829
|
+
content: {
|
|
2830
|
+
/**
|
|
2831
|
+
* @example {
|
|
2832
|
+
* "error": "Too many requests. Please try again later.",
|
|
2833
|
+
* "code": "rate_limit_exceeded",
|
|
2834
|
+
* "message": "Too many requests. Please try again later."
|
|
2835
|
+
* }
|
|
2836
|
+
*/
|
|
2837
|
+
"application/json": components["schemas"]["Error"];
|
|
2838
|
+
};
|
|
2839
|
+
};
|
|
2777
2840
|
/** @description The read could not be answered right now. Never cached; try again. */
|
|
2778
2841
|
503: {
|
|
2779
2842
|
headers: {
|
|
@@ -2852,6 +2915,21 @@ export interface operations {
|
|
|
2852
2915
|
};
|
|
2853
2916
|
};
|
|
2854
2917
|
};
|
|
2918
|
+
/** @description The request came with the user agent of an AI or LLM crawler, and no cached answer was there to serve instead. Never sent to a browser or to a server using its own user agent. */
|
|
2919
|
+
403: {
|
|
2920
|
+
headers: {
|
|
2921
|
+
[name: string]: unknown;
|
|
2922
|
+
};
|
|
2923
|
+
content: {
|
|
2924
|
+
/**
|
|
2925
|
+
* @example {
|
|
2926
|
+
* "error": "ai_bot_blocked",
|
|
2927
|
+
* "message": "AI/LLM scraping is not permitted. Use our API with a developer-tier API key for programmatic access."
|
|
2928
|
+
* }
|
|
2929
|
+
*/
|
|
2930
|
+
"application/json": components["schemas"]["Error"];
|
|
2931
|
+
};
|
|
2932
|
+
};
|
|
2855
2933
|
/** @description No verified page for that ticker on that chain, or an unknown chain */
|
|
2856
2934
|
404: {
|
|
2857
2935
|
headers: {
|
|
@@ -2868,6 +2946,24 @@ export interface operations {
|
|
|
2868
2946
|
"application/json": components["schemas"]["Error"];
|
|
2869
2947
|
};
|
|
2870
2948
|
};
|
|
2949
|
+
/** @description More than 400 requests in a minute from one IP address, across the whole API. Never cached. */
|
|
2950
|
+
429: {
|
|
2951
|
+
headers: {
|
|
2952
|
+
/** @description Seconds to wait before trying again */
|
|
2953
|
+
"Retry-After"?: number;
|
|
2954
|
+
[name: string]: unknown;
|
|
2955
|
+
};
|
|
2956
|
+
content: {
|
|
2957
|
+
/**
|
|
2958
|
+
* @example {
|
|
2959
|
+
* "error": "Too many requests. Please try again later.",
|
|
2960
|
+
* "code": "rate_limit_exceeded",
|
|
2961
|
+
* "message": "Too many requests. Please try again later."
|
|
2962
|
+
* }
|
|
2963
|
+
*/
|
|
2964
|
+
"application/json": components["schemas"]["Error"];
|
|
2965
|
+
};
|
|
2966
|
+
};
|
|
2871
2967
|
/** @description The read could not be answered right now. Never cached; try again. */
|
|
2872
2968
|
503: {
|
|
2873
2969
|
headers: {
|
|
@@ -2932,6 +3028,21 @@ export interface operations {
|
|
|
2932
3028
|
};
|
|
2933
3029
|
};
|
|
2934
3030
|
};
|
|
3031
|
+
/** @description The request came with the user agent of an AI or LLM crawler, and no cached answer was there to serve instead. Never sent to a browser or to a server using its own user agent. */
|
|
3032
|
+
403: {
|
|
3033
|
+
headers: {
|
|
3034
|
+
[name: string]: unknown;
|
|
3035
|
+
};
|
|
3036
|
+
content: {
|
|
3037
|
+
/**
|
|
3038
|
+
* @example {
|
|
3039
|
+
* "error": "ai_bot_blocked",
|
|
3040
|
+
* "message": "AI/LLM scraping is not permitted. Use our API with a developer-tier API key for programmatic access."
|
|
3041
|
+
* }
|
|
3042
|
+
*/
|
|
3043
|
+
"application/json": components["schemas"]["Error"];
|
|
3044
|
+
};
|
|
3045
|
+
};
|
|
2935
3046
|
/** @description An unknown chain, or an address that is not one on that chain */
|
|
2936
3047
|
404: {
|
|
2937
3048
|
headers: {
|
|
@@ -2966,9 +3077,15 @@ export interface operations {
|
|
|
2966
3077
|
"application/json": components["schemas"]["Error"];
|
|
2967
3078
|
};
|
|
2968
3079
|
};
|
|
2969
|
-
/**
|
|
3080
|
+
/**
|
|
3081
|
+
* @description The read could not be answered right now. Never cached; try again. It never means the wallet
|
|
3082
|
+
* holds nothing. In a busy moment, when Chain Daddy is making as many chain reads as it allows,
|
|
3083
|
+
* it carries `Retry-After`.
|
|
3084
|
+
*/
|
|
2970
3085
|
503: {
|
|
2971
3086
|
headers: {
|
|
3087
|
+
/** @description Seconds to wait before trying again. Sent only in a busy moment */
|
|
3088
|
+
"Retry-After"?: number;
|
|
2972
3089
|
[name: string]: unknown;
|
|
2973
3090
|
};
|
|
2974
3091
|
content: {
|
|
@@ -3023,7 +3140,7 @@ export interface operations {
|
|
|
3023
3140
|
chain: string;
|
|
3024
3141
|
chainId: string;
|
|
3025
3142
|
crownId: number;
|
|
3026
|
-
/** @description The coin's ticker
|
|
3143
|
+
/** @description The coin's ticker */
|
|
3027
3144
|
symbol: string;
|
|
3028
3145
|
badgeHosts: string[];
|
|
3029
3146
|
embedHosts: string[];
|
|
@@ -3031,7 +3148,22 @@ export interface operations {
|
|
|
3031
3148
|
};
|
|
3032
3149
|
};
|
|
3033
3150
|
};
|
|
3034
|
-
/** @description
|
|
3151
|
+
/** @description The request came with the user agent of an AI or LLM crawler, and no cached answer was there to serve instead. Never sent to a browser or to a server using its own user agent. */
|
|
3152
|
+
403: {
|
|
3153
|
+
headers: {
|
|
3154
|
+
[name: string]: unknown;
|
|
3155
|
+
};
|
|
3156
|
+
content: {
|
|
3157
|
+
/**
|
|
3158
|
+
* @example {
|
|
3159
|
+
* "error": "ai_bot_blocked",
|
|
3160
|
+
* "message": "AI/LLM scraping is not permitted. Use our API with a developer-tier API key for programmatic access."
|
|
3161
|
+
* }
|
|
3162
|
+
*/
|
|
3163
|
+
"application/json": components["schemas"]["Error"];
|
|
3164
|
+
};
|
|
3165
|
+
};
|
|
3166
|
+
/** @description An unknown chain, an ID that is not a positive whole number, or a registration that is not a verified coin page's */
|
|
3035
3167
|
404: {
|
|
3036
3168
|
headers: {
|
|
3037
3169
|
[name: string]: unknown;
|
|
@@ -3047,6 +3179,24 @@ export interface operations {
|
|
|
3047
3179
|
"application/json": components["schemas"]["Error"];
|
|
3048
3180
|
};
|
|
3049
3181
|
};
|
|
3182
|
+
/** @description More than 400 requests in a minute from one IP address, across the whole API. Never cached. */
|
|
3183
|
+
429: {
|
|
3184
|
+
headers: {
|
|
3185
|
+
/** @description Seconds to wait before trying again */
|
|
3186
|
+
"Retry-After"?: number;
|
|
3187
|
+
[name: string]: unknown;
|
|
3188
|
+
};
|
|
3189
|
+
content: {
|
|
3190
|
+
/**
|
|
3191
|
+
* @example {
|
|
3192
|
+
* "error": "Too many requests. Please try again later.",
|
|
3193
|
+
* "code": "rate_limit_exceeded",
|
|
3194
|
+
* "message": "Too many requests. Please try again later."
|
|
3195
|
+
* }
|
|
3196
|
+
*/
|
|
3197
|
+
"application/json": components["schemas"]["Error"];
|
|
3198
|
+
};
|
|
3199
|
+
};
|
|
3050
3200
|
/** @description The read could not be answered right now. Never cached; try again. */
|
|
3051
3201
|
503: {
|
|
3052
3202
|
headers: {
|
|
@@ -4412,7 +4562,8 @@ export interface operations {
|
|
|
4412
4562
|
* @description Sold out, or the wallet has reached the SKU's per-wallet limit. For an app's item: `APP_TERMS_CHANGED`
|
|
4413
4563
|
* (its developer changed its terms and the page's owner has not accepted them yet), `APP_NOT_INSTALLED`,
|
|
4414
4564
|
* `APP_NOT_SELLING`, `APP_ORDER_LIMIT` (above the per-purchase limit) or `APP_DAILY_LIMIT` (the wallet
|
|
4415
|
-
* reached what it may pay that app in 24 hours)
|
|
4565
|
+
* reached what it may pay that app in 24 hours). For an app's item paid through a checkout session:
|
|
4566
|
+
* `APP_ITEM_NOT_SOLD_HERE`, with a `reason`
|
|
4416
4567
|
*/
|
|
4417
4568
|
409: {
|
|
4418
4569
|
headers: {
|
|
@@ -4599,7 +4750,13 @@ export interface operations {
|
|
|
4599
4750
|
"application/json": components["schemas"]["IapError"];
|
|
4600
4751
|
};
|
|
4601
4752
|
};
|
|
4602
|
-
/**
|
|
4753
|
+
/**
|
|
4754
|
+
* @description `SKU_NOT_FOR_SALE`: the item is archived, has no price or is an unlock. `APP_ITEM_NOT_SOLD_HERE`: the
|
|
4755
|
+
* item belongs to an app installed on the coin's page and cannot be sold from the coin's own site now.
|
|
4756
|
+
* `reason` says why: `not_allowed_outside_app` (the app's developer has not turned it on for this item),
|
|
4757
|
+
* `app_not_installed`, `terms_changed` (the coin's owner must accept the app's new terms on the coin's
|
|
4758
|
+
* page), `item_not_for_sale` (the app no longer sells it), or one of the `APP_NOT_SELLING` reasons
|
|
4759
|
+
*/
|
|
4603
4760
|
409: {
|
|
4604
4761
|
headers: {
|
|
4605
4762
|
[name: string]: unknown;
|
package/dist/cd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var S="0.3.
|
|
1
|
+
"use strict";(()=>{var S="0.3.1";function v(t){let e="";for(let r of t)e+=String.fromCharCode(r);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function f(){return v(crypto.getRandomValues(new Uint8Array(32)))}function R(t,e,r,s,o){let d=n=>{if(n&&typeof n=="object"&&n.type===t)try{r(n)}catch{}},u=n=>{n.origin===e&&(!o||n.source===o)&&d(n.data)};window.addEventListener("message",u);let c=null;if(s&&typeof BroadcastChannel=="function"&&e===location.origin)try{c=new BroadcastChannel(s),c.onmessage=n=>d(n.data)}catch{c=null}return()=>{window.removeEventListener("message",u),c?.close()}}var m={get(t){try{return sessionStorage.getItem(t)}catch{return null}},set(t,e){try{sessionStorage.setItem(t,e)}catch{}},remove(t){try{sessionStorage.removeItem(t)}catch{}}};var q="https://api.chaindaddy.io",V={"https://api.chaindaddy.io":"https://chaindaddy.io/_oauth/authorize","https://staging-api.chaindaddy.io":"https://staging.chaindaddy.io/_oauth/authorize"},w="cd-signin",T="cd:signin",y="cd.signin";async function F(t){let e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return v(new Uint8Array(e))}function G(t="openid"){let e=t.split(/\s+/).filter(Boolean);return(e.includes("openid")?e:["openid",...e]).join(" ")}async function K(t,e,r){let s=new URLSearchParams({response_type:"code",client_id:e.clientId,redirect_uri:e.redirectUri,scope:G(t.scope),state:e.state,nonce:e.nonce,code_challenge:await F(e.verifier),code_challenge_method:"S256"});return t.claims&&s.set("claims",typeof t.claims=="string"?t.claims:JSON.stringify(t.claims)),`${r}?${s}`}function _(t,e){return{code:e,codeVerifier:t.verifier,state:t.state,nonce:t.nonce,issuer:t.issuer,clientId:t.clientId,redirectUri:t.redirectUri}}function b(t){let e=(t.issuer??q).replace(/\/$/,""),r=t.authorizeUrl??V[e];if(!r)return Promise.reject(new Error("authorizeUrl is required for this issuer"));if(!t.clientId||!t.redirectUri)return Promise.reject(new Error("clientId and redirectUri are required"));let s={state:f(),nonce:f(),verifier:f(),issuer:e,clientId:t.clientId,redirectUri:t.redirectUri};m.set(y,JSON.stringify(s));let o=window.open("",w,"popup,width=480,height=720");return(async()=>{let d=await K(t,s,r);if(!o)return location.assign(d),null;o.location.href=d;let u=new URL(t.redirectUri,location.href).origin;return new Promise((c,n)=>{let a=R(T,u,i=>{i.state===s.state&&(a(),m.remove(y),typeof i.error=="string"&&i.error?n(new Error(i.error)):i.iss!==e?n(new Error("the answer came from another issuer")):typeof i.code!="string"||!i.code?n(new Error("the answer carried no code")):c(_(s,i.code)))},w,o);t.signal?.addEventListener("abort",()=>{a(),n(new Error("aborted"))})})})()}async function U(){let t=new URLSearchParams(location.search),e={type:T,code:t.get("code"),state:t.get("state"),iss:t.get("iss"),error:t.get("error")};if(window.name===w){try{window.opener?.postMessage(e,location.origin)}catch{}try{let o=new BroadcastChannel(w);o.postMessage(e),o.close()}catch{}return window.close(),null}let r=m.get(y);m.remove(y);let s=r?JSON.parse(r):null;if(!s||e.state!==s.state)throw new Error("the sign-in state does not match: start again");if(e.error)throw new Error(e.error);if(e.iss!==s.issuer)throw new Error("the answer came from another issuer");if(!e.code)throw new Error("the answer carried no code");return _(s,e.code)}async function A(t){let e=await fetch(`${t.issuer}/api/v2/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:t.code,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:t.codeVerifier})}),r=await e.json();if(!e.ok)throw new Error(`${r.error??e.status}: ${r.error_description??""}`.trim());return r}var W='<svg viewBox="0 0 24 24" fill="#FFD700" width="20" height="20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5z" stroke="#B8860B" stroke-width="0.5"/><path d="M5 18h14v2H5v-2z" stroke="#B8860B" stroke-width="0.5"/></svg>',Y=`
|
|
2
2
|
:host { display: inline-block; }
|
|
3
3
|
button { display: inline-flex; align-items: center; justify-content: center; gap: 10px; box-sizing: border-box;
|
|
4
4
|
min-height: 40px; padding: 0 16px; border-radius: 10px; font: 600 15px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
@@ -7,7 +7,7 @@ button[data-theme="dark"] { background: #101828; color: #ffffff; border-color: #
|
|
|
7
7
|
button:focus-visible { outline: 2px solid #f59e0b; outline-offset: 2px; }
|
|
8
8
|
button:disabled { opacity: .6; cursor: default; }
|
|
9
9
|
svg { flex: none; }
|
|
10
|
-
`;function L(){if(typeof customElements>"u"||customElements.get("cd-signin-button"))return;class t extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){let
|
|
10
|
+
`;function L(){if(typeof customElements>"u"||customElements.get("cd-signin-button"))return;class t extends HTMLElement{constructor(){super(),this.root=this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(){this.isConnected&&this.render()}render(){let r=this.getAttribute("text")==="continue"?"Continue with Chain Daddy":"Sign in with Chain Daddy",s=this.getAttribute("theme")==="dark"?"dark":"light";this.root.innerHTML=`<style>${Y}</style><button type="button" part="button" data-theme="${s}">${W}<span></span></button>`;let o=this.root.querySelector("button");o.querySelector("span").textContent=r,o.addEventListener("click",()=>this.start())}start(){let r=s=>this.getAttribute(s)||void 0;b({clientId:r("client-id")??"",redirectUri:r("redirect-uri")??"",scope:r("scope"),claims:r("claims"),issuer:r("issuer"),authorizeUrl:r("authorize-url")}).then(s=>s&&this.dispatchEvent(new CustomEvent("cd-signin",{detail:s,bubbles:!0,composed:!0})),s=>this.dispatchEvent(new CustomEvent("cd-signin-error",{detail:{message:String(s?.message??s)},bubbles:!0,composed:!0})))}}t.observedAttributes=["theme","text"],customElements.define("cd-signin-button",t)}var M="https://embed.chaindaddy.io",k="https://embed-staging.chaindaddy.io";var J=["--cd-accent","--cd-surface","--cd-text","--cd-text-muted","--cd-border"];function P(t){try{if(t&&new URL(t).hostname==="js.staging.chaindaddy.io")return k}catch{}return M}var X=`
|
|
11
11
|
:host { display: block; }
|
|
12
12
|
iframe { display: block; width: 100%; border: 0; height: 1px; opacity: 0; color-scheme: normal; }
|
|
13
13
|
:host([data-cd-ready]) iframe { opacity: 1; }
|
|
@@ -19,4 +19,4 @@ button { box-sizing: border-box; min-height: 40px; padding: 0 16px; border-radiu
|
|
|
19
19
|
font: 600 15px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
20
20
|
border: 1px solid var(--cd-border, #d0d5dd); background: var(--cd-accent, #f59e0b); color: var(--cd-text, #101828); }
|
|
21
21
|
button:focus-visible { outline: 2px solid var(--cd-accent, #f59e0b); outline-offset: 2px; }
|
|
22
|
-
`;function O(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===k?k:M;class
|
|
22
|
+
`;function O(t={}){try{if(typeof customElements>"u"||typeof HTMLElement>"u")return;let e=t.origin===k?k:M;class r extends HTMLElement{constructor(){super(...arguments);this.kind="app";this.frame=null;this.onMessage=n=>this.receive(n)}connectedCallback(){try{this.shadowRoot||this.attachShadow({mode:"open"}),this.mount()}catch{this.unavailable()}}disconnectedCallback(){try{window.removeEventListener("message",this.onMessage),clearTimeout(this.timer)}catch{}}attributeChangedCallback(){try{this.isConnected&&this.shadowRoot&&this.mount()}catch{this.unavailable()}}src(){let n=(this.getAttribute("crown")??"").trim(),a=(this.getAttribute("chain")??"").trim(),i=(this.getAttribute("app")??"").trim();if(!/^[0-9]{1,18}$/.test(n)||!a||this.kind==="app"&&!i)return null;let l=new URLSearchParams({crown:n,chain:a});return this.kind==="app"?l.set("app",i):l.set("kind","coin"),l.set("mode",this.mode()),l.set("v","1"),`${e}/v1/frame?${l.toString()}`}mode(){let n=this.getAttribute("mode");if(n==="light"||n==="dark")return n;try{return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}catch{return"light"}}mount(){let n=this.shadowRoot;window.removeEventListener("message",this.onMessage),clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.removeAttribute("data-cd-unavailable");let a=this.src();if(!a){n.innerHTML="<slot></slot>",this.frame=null;return}n.innerHTML=`<style>${X}</style><slot></slot>`;let i=document.createElement("iframe");i.setAttribute("loading","lazy"),i.setAttribute("referrerpolicy","strict-origin-when-cross-origin"),i.setAttribute("allow","clipboard-write; fullscreen"),i.setAttribute("title",this.kind==="coin"?"Coin on Chain Daddy":"App on Chain Daddy"),i.src=a,n.appendChild(i),this.frame=i,window.addEventListener("message",this.onMessage),this.timer=setTimeout(()=>{this.hasAttribute("data-cd-ready")||this.unavailable()},8e3)}receive(n){try{if(n.origin!==e||!this.frame||!n.source||n.source!==this.frame.contentWindow)return;let a=n.data;if(!a||typeof a!="object")return;if(a.type==="cd:ready")clearTimeout(this.timer),this.removeAttribute("data-cd-unavailable"),this.setAttribute("data-cd-ready",""),this.sendTheme(),this.dispatchEvent(new CustomEvent("cd:ready"));else if(a.type==="cd:resize"){let i=Number(a.height);Number.isFinite(i)&&(this.frame.style.height=`${Math.max(1,Math.min(4e3,Math.ceil(i)))}px`)}else a.type==="cd:unavailable"&&this.unavailable()}catch{}}sendTheme(){let n={},a=getComputedStyle(this);for(let i of J){let l=a.getPropertyValue(i).trim();l&&(n[i]=l)}this.frame?.contentWindow?.postMessage({type:"cd:theme",tokens:n},e)}unavailable(){try{if(clearTimeout(this.timer),this.removeAttribute("data-cd-ready"),this.hasAttribute("data-cd-unavailable"))return;this.setAttribute("data-cd-unavailable",""),this.dispatchEvent(new CustomEvent("cd:unavailable"))}catch{}}}r.observedAttributes=["crown","chain","app","mode"];class s extends r{}class o extends r{constructor(){super(...arguments);this.kind="coin"}}class d extends HTMLElement{connectedCallback(){try{let c=this.querySelector("a[href]"),n=c?new URL(c.getAttribute("href")??"",document.baseURI):null;if(!n||n.protocol!=="https:")return;let a=this.shadowRoot??this.attachShadow({mode:"open"});a.innerHTML=`<style>${Q}</style><button type="button" part="button"></button>`;let i=a.querySelector("button");i.textContent=(c.textContent??"").trim()||"Buy on Chain Daddy",i.addEventListener("click",()=>{try{window.open(n.toString(),"cd-buy","popup,width=460,height=760")||window.location.assign(n.toString())}catch{}})}catch{}}}for(let[u,c]of[["cd-app",s],["cd-coin",o],["cd-buy",d]])customElements.get(u)||customElements.define(u,c)}catch{}}var Z=["https://chaindaddy.io","https://staging.chaindaddy.io"],D=()=>"cd-checkout-"+f(),H="popup,width=480,height=820",$=300,tt=7;function E(t){if(typeof t!="string")return null;let e;try{e=new URL(t)}catch{return null}let r=/^\/_pay\/([\w-]+)$/.exec(e.pathname);return r&&Z.includes(e.origin)&&!e.username&&!e.password?{href:e.origin+e.pathname,origin:e.origin,id:r[1]}:null}function C(t,e){if(typeof t!="string")return null;let r;try{r=new URL(t,location.href)}catch{return null}if(!/^https?:$/.test(r.protocol)||r.username||r.password)return null;let s=d=>{let u=d.split("=")[0];try{return decodeURIComponent(u.replace(/\+/g," "))}catch{return u}},o=r.search.slice(1).split("&").filter(d=>d&&s(d)!=="session_id");return o.push("session_id="+encodeURIComponent(e)),r.origin+r.pathname+"?"+o.join("&")+r.hash}var p;function N(t,e,r){return new Promise(s=>{let o=!1,d=0,u,c=l=>{clearInterval(i),clearTimeout(u),window.removeEventListener("message",n),r&&l.status==="completed"&&location.assign(r),s(l)},n=l=>{if(l.origin!==e.origin||l.source!==t)return;let g=l.data;if(!g||typeof g!="object")return;g.type==="cd:checkout.ready"&&(o=!0);let h=g.session;if(!(!h||typeof h!="object"||h.id!==e.id)){if(g.type==="cd:checkout.cancelled")c({status:"cancelled",session:{id:e.id}});else if(g.type==="cd:checkout.completed"&&(h.status==="complete"||h.status==="paid")){let I={id:e.id,status:h.status};typeof h.orderId=="string"&&/^[\w-]{1,128}$/.test(h.orderId)&&(I.orderId=h.orderId),c({status:"completed",session:I})}}},a=()=>{try{t.postMessage({type:"cd:checkout.hello"},e.origin)}catch{}},i=setInterval(()=>{t.closed?(clearInterval(i),u=setTimeout(()=>c({status:"closed"}),$)):(!o||++d%tt===0)&&a()},$);window.addEventListener("message",n),a()})}function x(t){let e=()=>new Error("checkout: url is not a Chain Daddy checkout session link"),r=(o,d,u)=>{p={popup:o,result:d,href:u};let c=()=>{p&&p.popup===o&&(p=void 0)};return d.then(c,c),d},s=o=>(location.assign(o.href),{status:"redirected"});try{let{url:o,successUrl:d}=t;if(d&&!C(d,""))throw new Error("checkout: successUrl is not a web address");let u=i=>d?C(d,i.id):null,c=typeof o=="function"?null:E(o);if(typeof o!="function"&&!c)throw e();if(p&&!p.popup.closed&&(!c||p.href===c.href))return p.popup.focus(),p.result;if(c){let i=window.open(c.href,D(),H);return i?r(i,N(i,c,u(c)),c.href):Promise.resolve(s(c))}let n=window.open("",D(),H),a=new Promise(i=>i(o())).then(i=>{let l=E(i);if(!l)throw n?.close(),e();return n?(n.location.href=l.href,N(n,l,u(l))):s(l)},i=>{throw n?.close(),i});return n?r(n,a):a}catch(o){return Promise.reject(o)}}var B=!1,z;function j(){B||typeof document>"u"||(B=!0,document.addEventListener("click",t=>{let e=t.target instanceof Element?t.target.closest("[data-cd-checkout]"):null;if(!e||t.defaultPrevented||t.button||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;let r=e.getAttribute("data-cd-checkout")||e.href,s=e.getAttribute("data-cd-success");if(!E(r)||s&&!C(s,""))return;t.preventDefault();let o=x({url:r,successUrl:s});o!==z&&(z=o,o.then(d=>void e.dispatchEvent(new CustomEvent("cd:checkout",{detail:d,bubbles:!0})),()=>{}))}))}var et={version:S,signIn:b,handleCallback:U,exchangeCode:A,checkout:x};window.ChainDaddy=Object.assign(window.ChainDaddy??{},et);L();O({origin:P(document.currentScript?.src)});j();})();
|
package/dist/cd.js.sri
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
sha384-
|
|
1
|
+
sha384-EF2XlOLUJ0J2U3uy/hlkmXdqyRP0WVm2LYjWdTseg/+QC4El1HD+qMhMlandYcSF
|
package/dist/core.d.ts
CHANGED
|
@@ -6,9 +6,10 @@ export declare function randomToken(): string;
|
|
|
6
6
|
/**
|
|
7
7
|
* Listens for messages of `type` from exactly `origin`, on window and on a
|
|
8
8
|
* same-origin BroadcastChannel (which still works when a COOP header has
|
|
9
|
-
* severed window.opener).
|
|
9
|
+
* severed window.opener). With `from`, a window message counts only when
|
|
10
|
+
* that window sent it. Returns a function that stops listening.
|
|
10
11
|
*/
|
|
11
|
-
export declare function listen(type: string, origin: string, onMessage: (data: Record<string, unknown>) => void, channel?: string): () => void;
|
|
12
|
+
export declare function listen(type: string, origin: string, onMessage: (data: Record<string, unknown>) => void, channel?: string, from?: Window | null): () => void;
|
|
12
13
|
/** sessionStorage that never throws (private windows, blocked storage). */
|
|
13
14
|
export declare const store: {
|
|
14
15
|
get(key: string): string | null;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/core.ts
|
|
2
|
-
var VERSION = true ? "0.3.
|
|
2
|
+
var VERSION = true ? "0.3.1" : "0.0.0";
|
|
3
3
|
function b64url(bytes) {
|
|
4
4
|
let s = "";
|
|
5
5
|
for (const b of bytes) s += String.fromCharCode(b);
|
|
@@ -8,7 +8,7 @@ function b64url(bytes) {
|
|
|
8
8
|
function randomToken() {
|
|
9
9
|
return b64url(crypto.getRandomValues(new Uint8Array(32)));
|
|
10
10
|
}
|
|
11
|
-
function listen(type, origin, onMessage, channel) {
|
|
11
|
+
function listen(type, origin, onMessage, channel, from) {
|
|
12
12
|
const accept = (data) => {
|
|
13
13
|
if (data && typeof data === "object" && data.type === type) {
|
|
14
14
|
try {
|
|
@@ -18,7 +18,7 @@ function listen(type, origin, onMessage, channel) {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
const onWindow = (e) => {
|
|
21
|
-
if (e.origin === origin) accept(e.data);
|
|
21
|
+
if (e.origin === origin && (!from || e.source === from)) accept(e.data);
|
|
22
22
|
};
|
|
23
23
|
window.addEventListener("message", onWindow);
|
|
24
24
|
let bc = null;
|
|
@@ -116,7 +116,7 @@ function signIn(o) {
|
|
|
116
116
|
else if (m.iss !== issuer) reject(new Error("the answer came from another issuer"));
|
|
117
117
|
else if (typeof m.code !== "string" || !m.code) reject(new Error("the answer carried no code"));
|
|
118
118
|
else resolve(result(p, m.code));
|
|
119
|
-
}, POPUP_NAME);
|
|
119
|
+
}, POPUP_NAME, popup);
|
|
120
120
|
o.signal?.addEventListener("abort", () => {
|
|
121
121
|
stop();
|
|
122
122
|
reject(new Error("aborted"));
|
package/dist/signin.js
CHANGED
|
@@ -7,7 +7,7 @@ function b64url(bytes) {
|
|
|
7
7
|
function randomToken() {
|
|
8
8
|
return b64url(crypto.getRandomValues(new Uint8Array(32)));
|
|
9
9
|
}
|
|
10
|
-
function listen(type, origin, onMessage, channel) {
|
|
10
|
+
function listen(type, origin, onMessage, channel, from) {
|
|
11
11
|
const accept = (data) => {
|
|
12
12
|
if (data && typeof data === "object" && data.type === type) {
|
|
13
13
|
try {
|
|
@@ -17,7 +17,7 @@ function listen(type, origin, onMessage, channel) {
|
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
const onWindow = (e) => {
|
|
20
|
-
if (e.origin === origin) accept(e.data);
|
|
20
|
+
if (e.origin === origin && (!from || e.source === from)) accept(e.data);
|
|
21
21
|
};
|
|
22
22
|
window.addEventListener("message", onWindow);
|
|
23
23
|
let bc = null;
|
|
@@ -115,7 +115,7 @@ function signIn(o) {
|
|
|
115
115
|
else if (m.iss !== issuer) reject(new Error("the answer came from another issuer"));
|
|
116
116
|
else if (typeof m.code !== "string" || !m.code) reject(new Error("the answer carried no code"));
|
|
117
117
|
else resolve(result(p, m.code));
|
|
118
|
-
}, POPUP_NAME);
|
|
118
|
+
}, POPUP_NAME, popup);
|
|
119
119
|
o.signal?.addEventListener("abort", () => {
|
|
120
120
|
stop();
|
|
121
121
|
reject(new Error("aborted"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaindaddy/js",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Chain Daddy for any website: Sign in with Chain Daddy (a popup and a button), a coin's apps on its own site (<cd-app>), checkout for a coin's store in a popup, and a server-side check that a store order was paid on chain",
|
|
5
5
|
"homepage": "https://docs.chaindaddy.io/developer/javascript-sdk",
|
|
6
6
|
"repository": {
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"esbuild": "^0.25.0",
|
|
62
62
|
"jsdom": "^26.0.0",
|
|
63
|
+
"openapi-typescript": "7.13.0",
|
|
63
64
|
"typescript": "^5.7.0",
|
|
64
65
|
"vitest": "^3.2.0"
|
|
65
66
|
},
|