@defdone/annon 0.0.0-stage → 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CLI.md +378 -0
  3. package/LICENSE +201 -0
  4. package/NOTICE +7 -0
  5. package/README.md +581 -2
  6. package/dist/api/annon-types.d.ts +108 -0
  7. package/dist/api/annon-types.js +2 -0
  8. package/dist/api/annon.d.ts +117 -0
  9. package/dist/api/annon.js +1152 -0
  10. package/dist/api/hosted-record.d.ts +13 -0
  11. package/dist/api/hosted-record.js +149 -0
  12. package/dist/api/sharing-types.d.ts +109 -0
  13. package/dist/api/sharing-types.js +1 -0
  14. package/dist/api/sharing.d.ts +36 -0
  15. package/dist/api/sharing.js +879 -0
  16. package/dist/browser/download.d.ts +19 -0
  17. package/dist/browser/download.js +52 -0
  18. package/dist/browser/index.d.ts +23 -0
  19. package/dist/browser/index.js +22 -0
  20. package/dist/browser/login-bundle.js +116 -0
  21. package/dist/browser/login.d.ts +1 -0
  22. package/dist/browser/login.js +177 -0
  23. package/dist/browser/purchase-bundle.js +84 -0
  24. package/dist/browser/purchases.d.ts +98 -0
  25. package/dist/browser/purchases.js +735 -0
  26. package/dist/browser/record.d.ts +55 -0
  27. package/dist/browser/record.js +16 -0
  28. package/dist/browser/selling.d.ts +146 -0
  29. package/dist/browser/selling.js +418 -0
  30. package/dist/browser/wallets.d.ts +53 -0
  31. package/dist/browser/wallets.js +286 -0
  32. package/dist/bytes.d.ts +4 -0
  33. package/dist/bytes.js +16 -0
  34. package/dist/download/download.d.ts +59 -0
  35. package/dist/download/download.js +384 -0
  36. package/dist/download/host.d.ts +56 -0
  37. package/dist/download/host.js +161 -0
  38. package/dist/download/http.d.ts +23 -0
  39. package/dist/download/http.js +87 -0
  40. package/dist/download/proof.d.ts +20 -0
  41. package/dist/download/proof.js +79 -0
  42. package/dist/download/upload.d.ts +130 -0
  43. package/dist/download/upload.js +408 -0
  44. package/dist/envelope/evm.d.ts +4 -0
  45. package/dist/envelope/evm.js +32 -0
  46. package/dist/envelope/pay2.d.ts +176 -0
  47. package/dist/envelope/pay2.js +541 -0
  48. package/dist/envelope/replay.d.ts +45 -0
  49. package/dist/envelope/replay.js +186 -0
  50. package/dist/envelope/stream.d.ts +78 -0
  51. package/dist/envelope/stream.js +372 -0
  52. package/dist/errors.d.ts +21 -0
  53. package/dist/errors.js +300 -0
  54. package/dist/index.d.ts +25 -0
  55. package/dist/index.js +23 -0
  56. package/dist/keys/near-ckd.d.ts +47 -0
  57. package/dist/keys/near-ckd.js +96 -0
  58. package/dist/limits.d.ts +4 -0
  59. package/dist/limits.js +4 -0
  60. package/dist/near/nep413.d.ts +22 -0
  61. package/dist/near/nep413.js +39 -0
  62. package/dist/near/rpc.d.ts +170 -0
  63. package/dist/near/rpc.js +423 -0
  64. package/dist/near/texts.d.ts +39 -0
  65. package/dist/near/texts.js +60 -0
  66. package/dist/net/ip.d.ts +7 -0
  67. package/dist/net/ip.js +62 -0
  68. package/dist/net/retry.d.ts +3 -0
  69. package/dist/net/retry.js +31 -0
  70. package/dist/node/adapter-checks.d.ts +6 -0
  71. package/dist/node/adapter-checks.js +13 -0
  72. package/dist/node/adapter.d.ts +8 -0
  73. package/dist/node/adapter.js +99 -0
  74. package/dist/node/agent.d.ts +49 -0
  75. package/dist/node/agent.js +266 -0
  76. package/dist/node/amounts.d.ts +2 -0
  77. package/dist/node/amounts.js +20 -0
  78. package/dist/node/browser-login.d.ts +18 -0
  79. package/dist/node/browser-login.js +212 -0
  80. package/dist/node/budget.d.ts +24 -0
  81. package/dist/node/budget.js +59 -0
  82. package/dist/node/buy-commands.d.ts +24 -0
  83. package/dist/node/buy-commands.js +301 -0
  84. package/dist/node/cli.d.ts +60 -0
  85. package/dist/node/cli.js +201 -0
  86. package/dist/node/command-context.d.ts +82 -0
  87. package/dist/node/command-context.js +77 -0
  88. package/dist/node/config.d.ts +37 -0
  89. package/dist/node/config.js +189 -0
  90. package/dist/node/core-adapter.d.ts +43 -0
  91. package/dist/node/core-adapter.js +122 -0
  92. package/dist/node/errors.d.ts +8 -0
  93. package/dist/node/errors.js +12 -0
  94. package/dist/node/index.d.ts +7 -0
  95. package/dist/node/index.js +5 -0
  96. package/dist/node/io.d.ts +13 -0
  97. package/dist/node/io.js +112 -0
  98. package/dist/node/keychain.d.ts +10 -0
  99. package/dist/node/keychain.js +67 -0
  100. package/dist/node/main.d.ts +2 -0
  101. package/dist/node/main.js +21 -0
  102. package/dist/node/offers.d.ts +16 -0
  103. package/dist/node/offers.js +91 -0
  104. package/dist/node/origin.d.ts +11 -0
  105. package/dist/node/origin.js +80 -0
  106. package/dist/node/paths.d.ts +3 -0
  107. package/dist/node/paths.js +71 -0
  108. package/dist/node/payment-terms.d.ts +26 -0
  109. package/dist/node/payment-terms.js +127 -0
  110. package/dist/node/purchase-commands.d.ts +16 -0
  111. package/dist/node/purchase-commands.js +237 -0
  112. package/dist/node/purchase-guard.d.ts +19 -0
  113. package/dist/node/purchase-guard.js +53 -0
  114. package/dist/node/relay-transport.d.ts +6 -0
  115. package/dist/node/relay-transport.js +173 -0
  116. package/dist/node/resources.d.ts +2 -0
  117. package/dist/node/resources.js +26 -0
  118. package/dist/node/retry-gate.d.ts +15 -0
  119. package/dist/node/retry-gate.js +53 -0
  120. package/dist/node/seller-commands.d.ts +12 -0
  121. package/dist/node/seller-commands.js +207 -0
  122. package/dist/node/sharing.d.ts +3 -0
  123. package/dist/node/sharing.js +28 -0
  124. package/dist/node/shops.d.ts +17 -0
  125. package/dist/node/shops.js +74 -0
  126. package/dist/node/signer-mock.d.ts +11 -0
  127. package/dist/node/signer-mock.js +79 -0
  128. package/dist/node/signer-wallet.d.ts +8 -0
  129. package/dist/node/signer-wallet.js +147 -0
  130. package/dist/node/signing.d.ts +29 -0
  131. package/dist/node/signing.js +200 -0
  132. package/dist/node/storage-commands.d.ts +11 -0
  133. package/dist/node/storage-commands.js +50 -0
  134. package/dist/node/sync-dir.d.ts +1 -0
  135. package/dist/node/sync-dir.js +11 -0
  136. package/dist/node/wallets.d.ts +13 -0
  137. package/dist/node/wallets.js +146 -0
  138. package/dist/oneclick/oneclick.d.ts +132 -0
  139. package/dist/oneclick/oneclick.js +342 -0
  140. package/dist/pay/backup.d.ts +70 -0
  141. package/dist/pay/backup.js +402 -0
  142. package/dist/pay/base.d.ts +135 -0
  143. package/dist/pay/base.js +393 -0
  144. package/dist/pay/eip3009.d.ts +59 -0
  145. package/dist/pay/eip3009.js +72 -0
  146. package/dist/pay/pay.d.ts +86 -0
  147. package/dist/pay/pay.js +297 -0
  148. package/dist/pay/purchase.d.ts +103 -0
  149. package/dist/pay/purchase.js +20 -0
  150. package/dist/pay/recover.d.ts +31 -0
  151. package/dist/pay/recover.js +34 -0
  152. package/dist/pay/seller.d.ts +130 -0
  153. package/dist/pay/seller.js +444 -0
  154. package/dist/pay/settle.d.ts +96 -0
  155. package/dist/pay/settle.js +308 -0
  156. package/dist/pay/split.d.ts +115 -0
  157. package/dist/pay/split.js +256 -0
  158. package/dist/pay/store-node.d.ts +8 -0
  159. package/dist/pay/store-node.js +224 -0
  160. package/dist/pay/store.d.ts +54 -0
  161. package/dist/pay/store.js +135 -0
  162. package/dist/pay/x402.d.ts +95 -0
  163. package/dist/pay/x402.js +136 -0
  164. package/dist/relay/admission.d.ts +8 -0
  165. package/dist/relay/admission.js +58 -0
  166. package/dist/relay/capabilities.d.ts +36 -0
  167. package/dist/relay/capabilities.js +232 -0
  168. package/dist/relay/fees.d.ts +17 -0
  169. package/dist/relay/fees.js +105 -0
  170. package/dist/relay/index.d.ts +10 -0
  171. package/dist/relay/index.js +8 -0
  172. package/dist/relay/keeper-balance.d.ts +17 -0
  173. package/dist/relay/keeper-balance.js +46 -0
  174. package/dist/relay/keeper-runner.d.ts +114 -0
  175. package/dist/relay/keeper-runner.js +703 -0
  176. package/dist/relay/keeper.d.ts +38 -0
  177. package/dist/relay/keeper.js +73 -0
  178. package/dist/relay/ledger.d.ts +155 -0
  179. package/dist/relay/ledger.js +392 -0
  180. package/dist/relay/relay-checks.d.ts +3 -0
  181. package/dist/relay/relay-checks.js +244 -0
  182. package/dist/relay/relay-reconciliation.d.ts +15 -0
  183. package/dist/relay/relay-reconciliation.js +293 -0
  184. package/dist/relay/relay-request.d.ts +6 -0
  185. package/dist/relay/relay-request.js +139 -0
  186. package/dist/relay/relay-transactions.d.ts +23 -0
  187. package/dist/relay/relay-transactions.js +182 -0
  188. package/dist/relay/relay-types.d.ts +128 -0
  189. package/dist/relay/relay-types.js +17 -0
  190. package/dist/relay/relay.d.ts +102 -0
  191. package/dist/relay/relay.js +426 -0
  192. package/dist/relay/reservation.d.ts +29 -0
  193. package/dist/relay/reservation.js +132 -0
  194. package/dist/relay/sponsorship.d.ts +42 -0
  195. package/dist/relay/sponsorship.js +184 -0
  196. package/dist/routes.d.ts +2 -0
  197. package/dist/routes.js +1 -0
  198. package/dist/settle-near/codec.d.ts +17 -0
  199. package/dist/settle-near/codec.js +86 -0
  200. package/dist/settle-near/identity.d.ts +27 -0
  201. package/dist/settle-near/identity.js +80 -0
  202. package/dist/settle-near/purchase.d.ts +40 -0
  203. package/dist/settle-near/purchase.js +153 -0
  204. package/dist/settle-near/terms.d.ts +37 -0
  205. package/dist/settle-near/terms.js +89 -0
  206. package/dist/shops/discovery.d.ts +95 -0
  207. package/dist/shops/discovery.js +384 -0
  208. package/dist/wallet/account-only.d.ts +11 -0
  209. package/dist/wallet/account-only.js +6 -0
  210. package/dist/wallet/uncertain.d.ts +8 -0
  211. package/dist/wallet/uncertain.js +13 -0
  212. package/dist/wallet/wallet.d.ts +100 -0
  213. package/dist/wallet/wallet.js +204 -0
  214. package/package.json +135 -3
@@ -0,0 +1,89 @@
1
+ /** Borsh commitments and seller texts, near-settlement.md §3. */
2
+ import { account, bytes, compare, concat, exactKeys, hash, le, network, str, uint, usdc, utc, } from './codec.js';
3
+ import { encodeIdentity, identityText } from './identity.js';
4
+ export const BASE_USDC = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913';
5
+ export const NEAR_USDC = '17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1';
6
+ const price = (p) => {
7
+ const n = uint(p, 128);
8
+ if (n === 0n)
9
+ throw new RangeError('price must be positive');
10
+ return le(n, 16);
11
+ };
12
+ export function encodeTerms(t, assets = {}) {
13
+ exactKeys(t, [
14
+ 'network',
15
+ 'contract',
16
+ 'file_id',
17
+ 'seller',
18
+ 'pay2_version',
19
+ 'cipher_hash',
20
+ 'moderator',
21
+ 'near',
22
+ 'base',
23
+ ]);
24
+ exactKeys(t.near, ['token', 'price', 'recipients', 'remainder']);
25
+ if (t.base !== null) {
26
+ exactKeys(t.base, ['usdc', 'price', 'pay_to']);
27
+ if (t.base.usdc !== (assets.baseUsdc ?? BASE_USDC))
28
+ throw new TypeError('invalid Base USDC');
29
+ }
30
+ network(t.network);
31
+ account(t.contract);
32
+ if (t.pay2_version !== 2)
33
+ throw new TypeError('unsupported PAY2 version');
34
+ const n = t.near;
35
+ account(n.token);
36
+ if (((assets.nearUsdc !== undefined || t.network === 'near:mainnet') &&
37
+ n.token !== (assets.nearUsdc ?? NEAR_USDC)) ||
38
+ n.recipients.length < 1 ||
39
+ n.recipients.length > 8 ||
40
+ !Number.isInteger(n.remainder) ||
41
+ n.remainder < 0 ||
42
+ n.remainder >= n.recipients.length)
43
+ throw new TypeError('invalid NEAR terms');
44
+ let prev;
45
+ let sum = 0;
46
+ const recipients = n.recipients.map((r) => {
47
+ exactKeys(r, ['identity', 'bps']);
48
+ const id = encodeIdentity(r.identity);
49
+ if (prev && compare(prev, id) >= 0)
50
+ throw new TypeError('recipients must be distinct and Borsh sorted');
51
+ prev = id;
52
+ if (!Number.isInteger(r.bps) || r.bps <= 0 || r.bps > 10000)
53
+ throw new TypeError('invalid bps');
54
+ sum += r.bps;
55
+ return concat(id, le(r.bps, 2));
56
+ });
57
+ if (sum !== 10000)
58
+ throw new TypeError('bps must sum to 10000');
59
+ return concat(str(t.network), str(t.contract), bytes(t.file_id, 32), encodeIdentity(t.seller), le(t.pay2_version, 1), bytes(t.cipher_hash, 32), t.moderator === null ? le(0, 1) : concat(le(1, 1), str(account(t.moderator))), str(n.token), price(n.price), le(recipients.length, 4), ...recipients, le(n.remainder, 1), t.base === null
60
+ ? le(0, 1)
61
+ : concat(le(1, 1), bytes(t.base.usdc, 20), price(t.base.price), bytes(t.base.pay_to, 20)));
62
+ }
63
+ export const termsHash = (t, assets) => hash('annon/terms/v2', encodeTerms(t, assets));
64
+ export function registerTextV2(t, sellerKey, deadline, assets) {
65
+ const commitment = termsHash(t, assets);
66
+ bytes(sellerKey, 48);
67
+ return [
68
+ 'Annon: list a file for sale',
69
+ 'Signing this lets whoever holds the seller key below open the file.',
70
+ `contract: ${t.contract}`,
71
+ `file: ${t.file_id}`,
72
+ `seller: ${identityText(t.seller)}`,
73
+ `price: ${usdc(t.near.price)} USDC on NEAR`,
74
+ ...t.near.recipients.map((r) => `split: ${identityText(r.identity)} ${(r.bps / 100).toFixed(2)}%`),
75
+ `remainder: ${identityText(t.near.recipients[t.near.remainder].identity)}`,
76
+ t.base ? `base: ${usdc(t.base.price)} USDC on Base to ${t.base.pay_to}` : 'base: none',
77
+ `cipher: ${t.cipher_hash}`,
78
+ `moderator: ${t.moderator ?? '(none)'}`,
79
+ `terms: ${commitment}`,
80
+ `seller key: ${sellerKey}`,
81
+ `valid until: ${utc(deadline)}`,
82
+ ].join('\n');
83
+ }
84
+ export function publishTextV2(contract, fileId, headerHash) {
85
+ account(contract);
86
+ bytes(fileId, 32);
87
+ bytes(headerHash, 32);
88
+ return `Annon: publish a file\ncontract: ${contract}\nfile: ${fileId}\nheader: ${headerHash}`;
89
+ }
@@ -0,0 +1,95 @@
1
+ import { type AnnonErrorCode } from '../errors.js';
2
+ import { type Terms } from '../settle-near/terms.js';
3
+ export interface ShopRules {
4
+ text: string;
5
+ uploadsPerPersonPerDay?: number;
6
+ attemptsPerPersonPerDay?: number;
7
+ fileLifetimeDays?: number | null;
8
+ maxFileBytes?: number;
9
+ }
10
+ export interface ShopManifest {
11
+ rules?: ShopRules;
12
+ version: 1;
13
+ name: string;
14
+ shopUrl: string;
15
+ network: 'near:mainnet';
16
+ contract: string;
17
+ relayer: string;
18
+ host: string;
19
+ catalog: {
20
+ url: string;
21
+ format: 'annon-catalog-array-v1';
22
+ search: 'client';
23
+ };
24
+ }
25
+ /** Deployment facts approved in local configuration, never copied from remote documents. */
26
+ export interface ShopProfile {
27
+ network: 'near:mainnet';
28
+ contract: string;
29
+ nearRpc: string;
30
+ baseRpc?: string;
31
+ relayer: string;
32
+ host: string;
33
+ }
34
+ export interface ConfiguredShop {
35
+ manifest: string;
36
+ id?: string;
37
+ profile?: ShopProfile;
38
+ }
39
+ export interface DiscoveryConfig {
40
+ shopDirectoryUrl?: string;
41
+ shops?: ConfiguredShop[];
42
+ defaultShop?: string;
43
+ }
44
+ export interface Shop {
45
+ manifest: string;
46
+ id?: string;
47
+ info: ShopManifest;
48
+ trusted: boolean;
49
+ }
50
+ export interface ShopFailure {
51
+ manifest: string;
52
+ id?: string;
53
+ trusted: boolean;
54
+ error: AnnonErrorCode;
55
+ }
56
+ export interface CatalogEntry {
57
+ name: string;
58
+ fileId: string;
59
+ title: string;
60
+ description: string;
61
+ url: string;
62
+ header: string;
63
+ byteSize: number;
64
+ plaintextSize: number;
65
+ createdAt: number;
66
+ expiresAt: number | null;
67
+ termsHash: string;
68
+ terms: Terms;
69
+ salesStopped: boolean;
70
+ takenDown: boolean;
71
+ }
72
+ export interface DiscoveryQuery {
73
+ search?: string;
74
+ limit?: number;
75
+ offset?: number;
76
+ }
77
+ export interface CatalogQuery extends DiscoveryQuery {
78
+ shop?: string;
79
+ }
80
+ export interface DiscoveryPage<T> {
81
+ items: T[];
82
+ total: number;
83
+ nextOffset: number | null;
84
+ }
85
+ export declare function canonicalManifestUrl(value: string): string;
86
+ export declare function validateDiscoveryConfig(value: unknown): DiscoveryConfig;
87
+ export declare function readShop(shop: ConfiguredShop, fetcher?: typeof fetch): Promise<Shop>;
88
+ export declare function listShops(config?: DiscoveryConfig, query?: DiscoveryQuery, fetcher?: typeof fetch): Promise<DiscoveryPage<Shop | ShopFailure>>;
89
+ export declare function catalog(config: DiscoveryConfig, query?: CatalogQuery, fetcher?: typeof fetch): Promise<DiscoveryPage<CatalogEntry> & {
90
+ shop?: {
91
+ manifest: string;
92
+ name: string;
93
+ };
94
+ rules?: ShopRules;
95
+ }>;
@@ -0,0 +1,384 @@
1
+ import { AnnonError, present, toPublicError } from '../errors.js';
2
+ import { MAX_PAY2_BYTES, MAX_PLAINTEXT_BYTES } from '../limits.js';
3
+ import { account, bytes, exactKeys } from '../settle-near/codec.js';
4
+ import { termsHash } from '../settle-near/terms.js';
5
+ const MANIFEST_PATH = '/.well-known/annon-shop.json';
6
+ function https(value) {
7
+ if (typeof value !== 'string')
8
+ throw new TypeError('invalid URL');
9
+ const url = new URL(value);
10
+ if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash)
11
+ throw new TypeError('invalid discovery URL');
12
+ return url.href;
13
+ }
14
+ function origin(value) {
15
+ const url = new URL(https(value));
16
+ if (url.pathname !== '/')
17
+ throw new TypeError('service must be an origin');
18
+ return url.origin;
19
+ }
20
+ export function canonicalManifestUrl(value) {
21
+ return new URL(MANIFEST_PATH, https(value)).href;
22
+ }
23
+ const alias = (value) => typeof value === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/.test(value);
24
+ function object(value) {
25
+ if (!value || typeof value !== 'object' || Array.isArray(value))
26
+ throw new TypeError('invalid object');
27
+ return value;
28
+ }
29
+ function text(value) {
30
+ if (typeof value !== 'string')
31
+ throw new TypeError('invalid text');
32
+ }
33
+ function integer(value, max = Number.MAX_SAFE_INTEGER) {
34
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0 || value > max)
35
+ throw new TypeError('invalid integer');
36
+ }
37
+ export function validateDiscoveryConfig(value) {
38
+ try {
39
+ const c = object(value);
40
+ const out = {};
41
+ if (c.shopDirectoryUrl !== undefined)
42
+ out.shopDirectoryUrl = https(c.shopDirectoryUrl);
43
+ if (c.shops !== undefined) {
44
+ if (!Array.isArray(c.shops) || c.shops.length > 100)
45
+ throw new TypeError('invalid shops');
46
+ const ids = new Set();
47
+ const urls = new Set();
48
+ out.shops = c.shops.map((item) => {
49
+ const s = object(item);
50
+ exactKeys(s, ['manifest', ...['id', 'profile'].filter((key) => Object.hasOwn(s, key))]);
51
+ const manifest = canonicalManifestUrl(s.manifest);
52
+ if (urls.has(manifest))
53
+ throw new TypeError('duplicate shop');
54
+ urls.add(manifest);
55
+ const shop = { manifest };
56
+ if (s.id !== undefined) {
57
+ if (!alias(s.id) || ids.has(s.id))
58
+ throw new TypeError('invalid alias');
59
+ ids.add(s.id);
60
+ shop.id = s.id;
61
+ }
62
+ if (s.profile !== undefined) {
63
+ const p = object(s.profile);
64
+ exactKeys(p, [
65
+ 'network',
66
+ 'contract',
67
+ 'nearRpc',
68
+ 'relayer',
69
+ 'host',
70
+ ...(Object.hasOwn(p, 'baseRpc') ? ['baseRpc'] : []),
71
+ ]);
72
+ if (p.network !== 'near:mainnet')
73
+ throw new TypeError('invalid profile network');
74
+ shop.profile = {
75
+ network: p.network,
76
+ contract: account(p.contract),
77
+ nearRpc: https(p.nearRpc),
78
+ ...(p.baseRpc !== undefined ? { baseRpc: https(p.baseRpc) } : {}),
79
+ relayer: origin(p.relayer),
80
+ host: origin(p.host),
81
+ };
82
+ }
83
+ return shop;
84
+ });
85
+ }
86
+ if (c.defaultShop !== undefined) {
87
+ text(c.defaultShop);
88
+ if (!out.shops?.some((s) => s.id === c.defaultShop || s.manifest === c.defaultShop))
89
+ throw new TypeError('unknown default shop');
90
+ out.defaultShop = c.defaultShop;
91
+ }
92
+ return out;
93
+ }
94
+ catch (cause) {
95
+ throw new AnnonError('invalid_config', undefined, { cause });
96
+ }
97
+ }
98
+ function validateRules(value) {
99
+ const r = object(value);
100
+ const keys = [
101
+ 'uploadsPerPersonPerDay',
102
+ 'attemptsPerPersonPerDay',
103
+ 'fileLifetimeDays',
104
+ 'maxFileBytes',
105
+ ];
106
+ exactKeys(r, ['text', ...keys.filter((key) => Object.hasOwn(r, key))]);
107
+ text(r.text);
108
+ if (!r.text.trim() ||
109
+ new TextEncoder().encode(r.text).byteLength > 4096 ||
110
+ /[\p{Cc}\p{Cs}\u2028\u2029]|(?![\u200c\u200d])\p{Cf}/u.test(r.text))
111
+ throw new TypeError('invalid rules text');
112
+ for (const key of keys) {
113
+ if (!Object.hasOwn(r, key))
114
+ continue;
115
+ if (key === 'fileLifetimeDays' && r[key] === null)
116
+ continue;
117
+ integer(r[key]);
118
+ }
119
+ return r;
120
+ }
121
+ function validateManifest(value) {
122
+ const m = object(value);
123
+ exactKeys(m, [
124
+ 'version',
125
+ 'name',
126
+ 'shopUrl',
127
+ 'network',
128
+ 'contract',
129
+ 'relayer',
130
+ 'host',
131
+ 'catalog',
132
+ ...(Object.hasOwn(m, 'rules') ? ['rules'] : []),
133
+ ]);
134
+ const c = object(m.catalog);
135
+ exactKeys(c, ['url', 'format', 'search']);
136
+ if (m.version !== 1 ||
137
+ m.network !== 'near:mainnet' ||
138
+ c.format !== 'annon-catalog-array-v1' ||
139
+ c.search !== 'client')
140
+ throw new TypeError('unsupported manifest');
141
+ text(m.name);
142
+ if (!m.name.trim())
143
+ throw new TypeError('missing shop name');
144
+ const host = origin(m.host);
145
+ const url = https(c.url);
146
+ if (new URL(url).origin !== host)
147
+ throw new TypeError('catalog origin mismatch');
148
+ return {
149
+ version: 1,
150
+ ...(Object.hasOwn(m, 'rules') ? { rules: validateRules(m.rules) } : {}),
151
+ name: m.name,
152
+ shopUrl: https(m.shopUrl),
153
+ network: m.network,
154
+ contract: account(m.contract),
155
+ relayer: origin(m.relayer),
156
+ host,
157
+ catalog: { url, format: c.format, search: c.search },
158
+ };
159
+ }
160
+ async function json(url, max, fetcher) {
161
+ try {
162
+ const r = await fetcher(https(url), {
163
+ credentials: 'omit',
164
+ redirect: 'error',
165
+ cache: 'no-store',
166
+ signal: AbortSignal.timeout(10000),
167
+ });
168
+ if (r.status !== 200 || r.redirected || (r.url && r.url !== url) || !r.body)
169
+ throw new AnnonError('bad_response');
170
+ const reader = r.body.getReader();
171
+ try {
172
+ if (Number(r.headers.get('content-length')) > max)
173
+ throw new AnnonError('too_large');
174
+ let size = 0;
175
+ const parts = [];
176
+ while (true) {
177
+ const { done, value } = await reader.read();
178
+ if (done)
179
+ break;
180
+ size += value.byteLength;
181
+ if (size > max)
182
+ throw new AnnonError('too_large');
183
+ parts.push(value);
184
+ }
185
+ const all = new Uint8Array(size);
186
+ let offset = 0;
187
+ for (const part of parts) {
188
+ all.set(part, offset);
189
+ offset += part.byteLength;
190
+ }
191
+ try {
192
+ return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(all));
193
+ }
194
+ catch (cause) {
195
+ throw new AnnonError('invalid_record', undefined, { cause });
196
+ }
197
+ }
198
+ finally {
199
+ await reader.cancel().catch(() => { });
200
+ }
201
+ }
202
+ catch (cause) {
203
+ if (cause instanceof AnnonError)
204
+ throw cause;
205
+ throw new AnnonError('transport', undefined, { cause });
206
+ }
207
+ }
208
+ export async function readShop(shop, fetcher = globalThis.fetch) {
209
+ const manifest = canonicalManifestUrl(shop.manifest);
210
+ let info;
211
+ try {
212
+ const raw = object(await json(manifest, 65536, fetcher));
213
+ const p = shop.profile;
214
+ if (p &&
215
+ (raw.network !== p.network ||
216
+ raw.contract !== p.contract ||
217
+ origin(raw.relayer) !== p.relayer ||
218
+ origin(raw.host) !== p.host ||
219
+ (raw.nearRpc !== undefined && https(raw.nearRpc) !== p.nearRpc) ||
220
+ (raw.baseRpc !== undefined && https(raw.baseRpc) !== p.baseRpc)))
221
+ throw new AnnonError('config_conflict');
222
+ info = validateManifest(raw);
223
+ }
224
+ catch (cause) {
225
+ if (cause instanceof AnnonError)
226
+ throw cause;
227
+ throw new AnnonError('invalid_record', undefined, { cause });
228
+ }
229
+ return {
230
+ manifest,
231
+ ...(shop.id !== undefined ? { id: shop.id } : {}),
232
+ info,
233
+ trusted: shop.profile !== undefined,
234
+ };
235
+ }
236
+ function paging(query) {
237
+ const { limit = 20, offset = 0, search = '' } = query;
238
+ if (!Number.isSafeInteger(limit) ||
239
+ limit < 1 ||
240
+ limit > 100 ||
241
+ !Number.isSafeInteger(offset) ||
242
+ offset < 0 ||
243
+ typeof search !== 'string')
244
+ throw new AnnonError('invalid_arguments');
245
+ return { limit, offset, search: search.toLowerCase() };
246
+ }
247
+ function page(all, { limit, offset }) {
248
+ const items = all.slice(offset, offset + limit);
249
+ const nextOffset = offset + items.length < all.length ? offset + items.length : null;
250
+ return { items, total: all.length, nextOffset };
251
+ }
252
+ async function configuredShops(config, fetcher) {
253
+ const shops = [...(config.shops ?? [])];
254
+ if (config.shopDirectoryUrl) {
255
+ const d = await json(config.shopDirectoryUrl, 262144, fetcher);
256
+ try {
257
+ exactKeys(d, ['version', 'shops']);
258
+ if (d.version !== 1 || !Array.isArray(d.shops))
259
+ throw new TypeError('invalid directory');
260
+ if (d.shops.length > 100)
261
+ throw new AnnonError('too_large');
262
+ for (const entry of d.shops) {
263
+ exactKeys(entry, ['manifest']);
264
+ const manifest = canonicalManifestUrl(entry.manifest);
265
+ if (!shops.some((s) => s.manifest === manifest))
266
+ shops.push({ manifest });
267
+ }
268
+ }
269
+ catch (cause) {
270
+ if (cause instanceof AnnonError)
271
+ throw cause;
272
+ throw new AnnonError('invalid_record', undefined, { cause });
273
+ }
274
+ }
275
+ return shops;
276
+ }
277
+ export async function listShops(config = {}, query = {}, fetcher = globalThis.fetch) {
278
+ const q = paging(query);
279
+ const c = validateDiscoveryConfig(config);
280
+ const shops = await configuredShops(c, fetcher);
281
+ const all = new Array(shops.length);
282
+ let next = 0;
283
+ const results = await Promise.allSettled(Array.from({ length: Math.min(4, shops.length) }, async () => {
284
+ while (next < shops.length) {
285
+ const index = next++;
286
+ const shop = present(shops[index]);
287
+ try {
288
+ all[index] = await readShop(shop, fetcher);
289
+ }
290
+ catch (error) {
291
+ if (shop.profile && error instanceof AnnonError && error.code === 'config_conflict')
292
+ throw error;
293
+ all[index] = {
294
+ manifest: shop.manifest,
295
+ ...(shop.id !== undefined ? { id: shop.id } : {}),
296
+ trusted: !!shop.profile,
297
+ error: toPublicError(error).code,
298
+ };
299
+ }
300
+ }
301
+ }));
302
+ for (const result of results)
303
+ if (result.status === 'rejected')
304
+ throw result.reason;
305
+ return page(all.filter((shop) => ('info' in shop ? shop.info.name : (shop.id ?? shop.manifest))
306
+ .toLowerCase()
307
+ .includes(q.search)), q);
308
+ }
309
+ function validateCatalog(value, shop) {
310
+ if (!Array.isArray(value))
311
+ throw new TypeError('invalid catalog');
312
+ if (value.length > 10000)
313
+ throw new AnnonError('too_large');
314
+ return value.map((item) => {
315
+ exactKeys(item, [
316
+ 'name',
317
+ 'fileId',
318
+ 'title',
319
+ 'description',
320
+ 'url',
321
+ 'header',
322
+ 'byteSize',
323
+ 'plaintextSize',
324
+ 'createdAt',
325
+ 'expiresAt',
326
+ 'termsHash',
327
+ 'terms',
328
+ 'salesStopped',
329
+ 'takenDown',
330
+ ]);
331
+ for (const key of ['name', 'title', 'description'])
332
+ text(item[key]);
333
+ bytes(item.fileId, 32);
334
+ bytes(item.termsHash, 32);
335
+ for (const key of ['url', 'header'])
336
+ if (new URL(https(item[key])).origin !== shop.host)
337
+ throw new AnnonError('config_conflict');
338
+ integer(item.byteSize, MAX_PAY2_BYTES);
339
+ integer(item.plaintextSize, MAX_PLAINTEXT_BYTES);
340
+ integer(item.createdAt);
341
+ if (item.expiresAt !== null) {
342
+ integer(item.expiresAt);
343
+ if (item.expiresAt < item.createdAt)
344
+ throw new TypeError('invalid expiry');
345
+ }
346
+ if (typeof item.salesStopped !== 'boolean' || typeof item.takenDown !== 'boolean')
347
+ throw new TypeError('invalid listing flags');
348
+ const terms = item.terms;
349
+ if (terms.contract !== shop.contract || terms.network !== shop.network)
350
+ throw new AnnonError('config_conflict');
351
+ if (terms.file_id !== item.fileId || termsHash(terms) !== item.termsHash)
352
+ throw new TypeError('invalid catalog terms');
353
+ return item;
354
+ });
355
+ }
356
+ export async function catalog(config, query = {}, fetcher = globalThis.fetch) {
357
+ const q = paging(query);
358
+ const c = validateDiscoveryConfig(config);
359
+ const selected = query.shop ?? c.defaultShop;
360
+ if (typeof selected !== 'string' || !selected)
361
+ throw new AnnonError('invalid_arguments');
362
+ const local = c.shops?.find((s) => s.id === selected || s.manifest === selected);
363
+ const found = local ?? (await configuredShops(c, fetcher)).find((s) => s.manifest === selected);
364
+ if (!found)
365
+ throw new AnnonError('invalid_arguments');
366
+ const shop = await readShop(found, fetcher);
367
+ let all;
368
+ try {
369
+ all = validateCatalog(await json(shop.info.catalog.url, 8388608, fetcher), shop.info);
370
+ }
371
+ catch (cause) {
372
+ if (cause instanceof AnnonError)
373
+ throw cause;
374
+ throw new AnnonError('invalid_record', undefined, { cause });
375
+ }
376
+ all = all.filter((e) => [e.name, e.title, e.description].some((s) => s.toLowerCase().includes(q.search)));
377
+ all.sort((a, b) => b.createdAt - a.createdAt || (a.fileId < b.fileId ? -1 : a.fileId > b.fileId ? 1 : 0));
378
+ return {
379
+ ...page(all, q),
380
+ ...(shop.info.rules
381
+ ? { shop: { manifest: shop.manifest, name: shop.info.name }, rules: shop.info.rules }
382
+ : {}),
383
+ };
384
+ }
@@ -0,0 +1,11 @@
1
+ /** Never supply contractId/methodNames: MyNearWallet otherwise generates a local secret. */
2
+ export declare function signInAccountOnly(wallet: {
3
+ signOut?: () => Promise<void>;
4
+ signIn(params: {
5
+ contractId?: string;
6
+ }): Promise<{
7
+ accountId: string;
8
+ }[]>;
9
+ }): Promise<{
10
+ accountId: string;
11
+ }[]>;
@@ -0,0 +1,6 @@
1
+ /** Never supply contractId/methodNames: MyNearWallet otherwise generates a local secret. */
2
+ export async function signInAccountOnly(wallet) {
3
+ // A reused loopback origin may contain older connector authority. Never let it sign silently.
4
+ await wallet.signOut?.();
5
+ return wallet.signIn({});
6
+ }
@@ -0,0 +1,8 @@
1
+ import { AnnonError } from '../errors.js';
2
+ /** Public evidence to retain whenever a submitted NEAR transaction cannot be confirmed. */
3
+ export declare class TransactionUncertain extends AnnonError {
4
+ readonly txHash: string;
5
+ readonly purchaseId?: string | undefined;
6
+ readonly code = "transaction_uncertain";
7
+ constructor(txHash: string, purchaseId?: string | undefined);
8
+ }
@@ -0,0 +1,13 @@
1
+ import { AnnonError } from '../errors.js';
2
+ /** Public evidence to retain whenever a submitted NEAR transaction cannot be confirmed. */
3
+ export class TransactionUncertain extends AnnonError {
4
+ txHash;
5
+ purchaseId;
6
+ code = 'transaction_uncertain';
7
+ constructor(txHash, purchaseId) {
8
+ super('transaction_uncertain', 'Transaction submission is uncertain. Check the saved purchase and transaction hash before retrying.');
9
+ this.txHash = txHash;
10
+ this.purchaseId = purchaseId;
11
+ this.name = 'TransactionUncertain';
12
+ }
13
+ }