@nibgate/sdk 0.2.6 → 0.2.8

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/SKILL.md CHANGED
@@ -42,37 +42,51 @@ If the widget loads after app code, the SDK queues events and flushes them when
42
42
 
43
43
  **Adblocker bypass:** The widget now sends events to `/api/hub/evt` instead of `/api/hub/track` to avoid adblocker filter lists that target the word "track". The old `/api/hub/track` endpoint is preserved for backward compatibility. If you self-host the backend, ensure both routes are registered.
44
44
 
45
+ **Hosted mode (zero backend code):** If you don't want to run your own access route, mark premium content with a data attribute and the widget handles everything:
46
+
47
+ ```html
48
+ <div data-nibgate-premium="0.01" data-nibgate-recipient="0xYourWallet">
49
+ <p>Teaser text shown to everyone...</p>
50
+ <div data-nibgate-unlock-card>
51
+ <span data-nibgate-wallet-label>No wallet</span>
52
+ <button data-nibgate-connect>Connect</button>
53
+ <button data-nibgate-unlock-btn>Unlock for 0.01 USDC</button>
54
+ <p data-nibgate-status></p>
55
+ </div>
56
+ <div data-nibgate-unlocked hidden>
57
+ Full premium content here...
58
+ </div>
59
+ </div>
60
+ ```
61
+
62
+ No SDK import, no server access route, no env vars. The default wallet is set once in the Nibgate dashboard — or override per-post with `data-nibgate-recipient`. Custom price per-post: change `data-nibgate-premium="0.05"`.
63
+
45
64
  ---
46
65
 
47
- ## 2. Resource Shape
66
+ ## 2. Resource Shape (Minimal)
48
67
 
49
- Define one stable resource per paid route, CMS row, media item, file, or API product:
68
+ The SDK auto-derives most fields from meta tags and defaults. You only need:
50
69
 
51
70
  ```ts
52
71
  const resource = {
53
72
  id: post.id,
54
- title: post.title,
55
- description: post.excerpt,
56
- type: 'article',
57
- price: '0.01',
58
- currency: 'USDC',
59
- recipient: post.creatorWallet,
73
+ title: post.title, // auto-fallsback to og:title → document.title
60
74
  path: `/posts/${post.slug}`,
61
- url: `https://creator.example/posts/${post.slug}`,
62
- tags: ['essay', 'research'],
63
- access: { humans: 'paid', agents: 'paid' },
64
- unlock: { mode: 'one_time' }
75
+ price: '0.01' // sets access: { humans: 'paid', agents: 'paid' } automatically
65
76
  }
66
77
  ```
67
78
 
68
- Allowed types: `article`, `image`, `music`, `video` (aliases like `audio→music`, `photo→image` are normalized). Use stable IDs — changing IDs breaks continuity for Explore, receipts, and reputation.
69
-
70
- **Auto-derived fields:** When running in a browser, the SDK auto-fills:
71
- - `url` from `window.location.origin + path` if `path` is provided but `url` is not
72
- - `imageUrl` from `<meta property="og:image">` if not explicitly set
73
- - `recipient` is **not required client-side** — the server's 402 payment challenge provides the canonical wallet address. Missing it generates a warning, not an error.
79
+ The rest fills in automatically from your page's HTML:
80
+ - `url` from `window.location.origin + path`
81
+ - `type` from `<meta property="og:type">` (defaults to `article`)
82
+ - `imageUrl` from `<meta property="og:image">`
83
+ - `description` from `<meta property="og:description">` `<meta name="description">`
84
+ - `tags` from `<meta name="keywords">`
85
+ - `currency` defaults to `USDC`
86
+ - `access` defaults to `paid` when `price > 0`, otherwise `free`
87
+ - `recipient` is optional (server's 402 challenge provides the real wallet)
74
88
 
75
- So a minimal client-side resource only needs `id`, `title`, `path`, and `price` — the rest is filled in automatically.
89
+ Allowed types: `article`, `image`, `music`, `video` (aliases like `audio→music`, `photo→image` are normalized). Use stable IDs changing IDs breaks continuity for Explore, receipts, and reputation.
76
90
 
77
91
  **Important:** The `image`, `description`, and `title` fields become the public thumbnail and card copy on the Explore page. Use a teaser preview, not the actual paid file.
78
92
 
package/dist/nibgate.js CHANGED
@@ -38,6 +38,27 @@ var Nibgate = (() => {
38
38
  __export(gateway_exports, {
39
39
  createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter
40
40
  });
41
+ async function getCircleClient(options = {}) {
42
+ if (cachedCircleModule) return cachedCircleModule;
43
+ if (options.clientModule) {
44
+ cachedCircleModule = options.clientModule;
45
+ return cachedCircleModule;
46
+ }
47
+ const moduleUrl = options.clientModuleUrl || "@circle-fin/x402-batching/client";
48
+ try {
49
+ cachedCircleModule = await import(moduleUrl);
50
+ return cachedCircleModule;
51
+ } catch (error) {
52
+ if (!options.clientModuleUrl) {
53
+ throw new Error(
54
+ `Could not load @circle-fin/x402-batching/client from node_modules. Provide a clientModuleUrl option (e.g. "https://esm.sh/@circle-fin/x402-batching@3.2.0/client?bundle") to load from CDN. Original error: ${error.message}`
55
+ );
56
+ }
57
+ throw new Error(
58
+ `Could not load Circle Gateway client from CDN: ${error.message}. Check that ${options.clientModuleUrl} is reachable.`
59
+ );
60
+ }
61
+ }
41
62
  function encodeBase64(value) {
42
63
  const text = typeof value === "string" ? value : stringifyJson(value);
43
64
  if (typeof Buffer !== "undefined") return Buffer.from(text).toString("base64");
@@ -52,7 +73,7 @@ var Nibgate = (() => {
52
73
  if (!signer?.address || typeof signer.signTypedData !== "function") {
53
74
  throw new Error("Circle Gateway browser adapter requires an EVM signer with address and signTypedData.");
54
75
  }
55
- const circleClientModule = options.clientModule || (options.clientModuleUrl ? await runtimeImport(options.clientModuleUrl) : await runtimeImport("@circle-fin/x402-batching/client"));
76
+ const circleClientModule = await getCircleClient(options);
56
77
  const { BatchEvmScheme } = circleClientModule;
57
78
  const scheme = new BatchEvmScheme(signer);
58
79
  const network = options.network || options.chainId && `eip155:${options.chainId}` || "eip155:5042002";
@@ -110,11 +131,11 @@ var Nibgate = (() => {
110
131
  }
111
132
  };
112
133
  }
113
- var runtimeImport;
134
+ var cachedCircleModule;
114
135
  var init_gateway = __esm({
115
136
  "src/browser/gateway.js"() {
116
137
  init_json();
117
- runtimeImport = new Function("specifier", "return import(specifier)");
138
+ cachedCircleModule = null;
118
139
  }
119
140
  });
120
141
 
@@ -137,6 +158,7 @@ var Nibgate = (() => {
137
158
  createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter2,
138
159
  createEvmGatewayUnlock: () => createEvmGatewayUnlock,
139
160
  createGate: () => createGate,
161
+ createHostedUnlock: () => createHostedUnlock,
140
162
  createNibgate: () => createNibgate,
141
163
  createNibgateContentSettings: () => createNibgateContentSettings,
142
164
  createOnchainRating: () => createOnchainRating,
@@ -314,7 +336,7 @@ var Nibgate = (() => {
314
336
  const normalized = normalizeResource(resource);
315
337
  const warnings = [];
316
338
  const errors = [];
317
- const required = options.required || ["id"];
339
+ const required = options.required || ["id", "type"];
318
340
  const recommended = options.recommended || ["title", "url", "description", "imageUrl", "tags"];
319
341
  for (const field of required) {
320
342
  if (!hasValue(normalized[field])) errors.push(`Missing required content metadata: ${field}`);
@@ -782,10 +804,16 @@ var Nibgate = (() => {
782
804
  const gateway = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
783
805
  return gateway.createCircleGatewayBrowserAdapter(options);
784
806
  }
807
+ var DEFAULT_CIRCLE_CDN = "https://esm.sh/@circle-fin/x402-batching@3.2.0/client?bundle";
808
+ var HOSTED_PAY_URL = "https://api.nibgate.xyz/api/hub/pay";
809
+ function resolveAccessPath(resource, options) {
810
+ if (options.hosted || options.accessPath === "hosted") return HOSTED_PAY_URL;
811
+ return options.accessPath || resource.accessPath || "/api/nibgate/access";
812
+ }
785
813
  function createEvmGatewayUnlock(resource, options = {}) {
786
814
  const item = createGate(resource, options.gateOptions || {});
787
815
  const win = browserWindow();
788
- const accessPath = options.accessPath || item.resource.accessPath || "/api/nibgate/access";
816
+ const accessPath = resolveAccessPath(item.resource, options);
789
817
  const source = options.source || "nibgate-evm-gateway";
790
818
  const network = options.network || "eip155:5042002";
791
819
  const statusTarget = typeof options.status === "string" ? win?.document.querySelector(options.status) : options.status;
@@ -876,7 +904,7 @@ var Nibgate = (() => {
876
904
  signTypedData: (typedData) => evm.request({ method: "eth_signTypedData_v4", params: [walletAddress, stringifyJson(typedData)] })
877
905
  },
878
906
  clientModule: options.circleClientModule,
879
- clientModuleUrl: options.circleClientModuleUrl
907
+ clientModuleUrl: options.circleClientModuleUrl || DEFAULT_CIRCLE_CDN
880
908
  });
881
909
  return gatewayWallet.pay(input);
882
910
  }
@@ -938,6 +966,14 @@ var Nibgate = (() => {
938
966
  if (options.autoMount !== false) mount();
939
967
  return controller;
940
968
  }
969
+ function createHostedUnlock(resource, options = {}) {
970
+ return createEvmGatewayUnlock(resource, {
971
+ ...options,
972
+ hosted: true,
973
+ circleClientModuleUrl: options.circleClientModuleUrl || DEFAULT_CIRCLE_CDN,
974
+ noWalletMessage: options.noWalletMessage || "Install MetaMask or another EVM wallet to unlock premium content."
975
+ });
976
+ }
941
977
 
942
978
  // src/browser/reputation.js
943
979
  var RATE_CONTENT_SELECTOR = "0xc62fad09";