@carddb/node 0.4.2 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,7 +38,7 @@ for await (const card of cards) {
38
38
 
39
39
  ## App-Owned Decks
40
40
 
41
- Use `secretKey` only in trusted server runtimes. App-owned deck helpers such as external-reference upsert and deck access token exchange require `secretKey` or the legacy `apiKey` option. If `accessToken` or `publishableKey` would be sent instead, those secret-only helpers fail before making a request.
41
+ Use `secretKey` only in trusted server runtimes. App-owned deck helpers such as external-reference upsert and deck token exchange require `secretKey` or the legacy `apiKey` option. If `accessToken` or `publishableKey` would be sent instead, those secret-only helpers fail before making a request.
42
42
 
43
43
  ```typescript
44
44
  const upsert = await client.decks.upsertByExternalRef({
@@ -49,37 +49,44 @@ const upsert = await client.decks.upsertByExternalRef({
49
49
  title: 'Course Deck',
50
50
  accessMode: 'AUTHORIZED_TOKEN',
51
51
  discoverability: 'UNLISTED',
52
+ // Omit entries to preserve an existing deck's entries on update.
53
+ // Pass [] to explicitly create or replace with an empty decklist.
52
54
  entries: [{ datasetKey: 'cards', identifier: 'CARD-001', quantity: 4 }],
53
55
  })
54
56
 
55
- const exchange = await client.decks.exchangeAccessToken({
57
+ const exchange = await client.decks.exchangeToken({
56
58
  deckId: upsert.deck.id,
57
- readMode: 'FULL',
59
+ scopes: ['DECK_PUBLISHED_READ'],
60
+ representation: 'DETAILED',
58
61
  externalSubject: 'user_123',
59
62
  })
63
+
64
+ const tokenClient = new CardDBClient({ accessToken: exchange.token })
65
+ const deck = await tokenClient.decks.getPublished(upsert.deck.id)
60
66
  ```
61
67
 
62
68
  ## Direct Signed Deck Tokens
63
69
 
64
- Trusted server runtimes can also mint direct signed deck access tokens locally after registering an issuer public key in CardDB.
70
+ Trusted server runtimes can also mint direct signed deck tokens locally after registering an issuer public key in CardDB.
65
71
 
66
72
  ```typescript
67
73
  import {
68
- generateDirectDeckAccessKeyPair,
69
- signDirectDeckAccessToken,
74
+ generateDirectDeckTokenKeyPair,
75
+ signDirectDeckToken,
70
76
  } from '@carddb/node'
71
77
 
72
- const keyPair = generateDirectDeckAccessKeyPair()
78
+ const keyPair = generateDirectDeckTokenKeyPair()
73
79
 
74
80
  console.log(keyPair.publicKeyBase64) // Register this on directSigningKey.publicKey
75
81
 
76
- const token = await signDirectDeckAccessToken({
82
+ const token = await signDirectDeckToken({
77
83
  issuerId: 'issuer_uuid',
78
84
  deckId: 'deck_uuid',
79
85
  apiApplicationId: 'app_uuid',
80
86
  keyId: 'prod-2026-01',
81
87
  privateKey: keyPair.privateKeyPem,
82
- readMode: 'FULL',
88
+ scopes: ['DECK_PUBLISHED_READ'],
89
+ representation: 'DETAILED',
83
90
  subject: 'user_123',
84
91
  expiresAt: new Date(Date.now() + 15 * 60 * 1000),
85
92
  })
package/dist/index.cjs CHANGED
@@ -66,9 +66,9 @@ var MemoryCache = class {
66
66
  }
67
67
  }
68
68
  };
69
- var DIRECT_DECK_ACCESS_TOKEN_USE = "carddb.deck_access.v1";
70
- var DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = "carddb.deck_access";
71
- function generateDirectDeckAccessKeyPair() {
69
+ var DIRECT_DECK_TOKEN_USE = "carddb.deck_token.v1";
70
+ var DIRECT_DECK_TOKEN_AUDIENCE = "carddb.deck";
71
+ function generateDirectDeckTokenKeyPair() {
72
72
  const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519");
73
73
  const jwk = publicKey.export({ format: "jwk" });
74
74
  if (!("x" in jwk) || typeof jwk.x !== "string") {
@@ -79,12 +79,13 @@ function generateDirectDeckAccessKeyPair() {
79
79
  publicKeyBase64: encodeBase64(decodeBase64URL(jwk.x))
80
80
  };
81
81
  }
82
- async function signDirectDeckAccessToken(input) {
82
+ async function signDirectDeckToken(input) {
83
83
  const issuedAt = input.issuedAt ?? /* @__PURE__ */ new Date();
84
84
  validateRequiredString(input.issuerId, "issuerId");
85
- validateRequiredString(input.deckId, "deckId");
86
85
  validateRequiredString(input.apiApplicationId, "apiApplicationId");
87
86
  validateRequiredString(input.keyId, "keyId");
87
+ validateScopes(input.scopes);
88
+ validateTokenTarget(input);
88
89
  if (!(issuedAt instanceof Date) || Number.isNaN(issuedAt.getTime())) {
89
90
  throw new TypeError("issuedAt must be a valid Date");
90
91
  }
@@ -100,15 +101,29 @@ async function signDirectDeckAccessToken(input) {
100
101
  kid: input.keyId.trim()
101
102
  };
102
103
  const payload = {
103
- token_use: DIRECT_DECK_ACCESS_TOKEN_USE,
104
- deck_id: input.deckId.trim(),
104
+ token_use: DIRECT_DECK_TOKEN_USE,
105
105
  app_id: input.apiApplicationId.trim(),
106
- read_mode: normalizeReadMode(input.readMode),
106
+ scopes: input.scopes,
107
107
  iss: input.issuerId.trim(),
108
- aud: DIRECT_DECK_ACCESS_TOKEN_AUDIENCE,
108
+ aud: DIRECT_DECK_TOKEN_AUDIENCE,
109
109
  iat: Math.floor(issuedAt.getTime() / 1e3),
110
110
  exp: Math.floor(input.expiresAt.getTime() / 1e3)
111
111
  };
112
+ if (input.deckId?.trim()) {
113
+ payload.deck_id = input.deckId.trim();
114
+ }
115
+ if (input.publisherSlug?.trim()) {
116
+ payload.publisher_slug = input.publisherSlug.trim();
117
+ }
118
+ if (input.gameKey?.trim()) {
119
+ payload.game_key = input.gameKey.trim();
120
+ }
121
+ if (input.externalSubject?.trim()) {
122
+ payload.external_subject = input.externalSubject.trim();
123
+ }
124
+ if (input.representation?.trim()) {
125
+ payload.representation = input.representation.trim();
126
+ }
112
127
  if (input.subject?.trim()) {
113
128
  payload.sub = input.subject.trim();
114
129
  }
@@ -126,18 +141,19 @@ function validateRequiredString(value, name) {
126
141
  throw new TypeError(`${name} is required`);
127
142
  }
128
143
  }
129
- function normalizeReadMode(readMode) {
130
- switch (readMode) {
131
- case "PUBLIC":
132
- return "public";
133
- case "EMBED":
134
- return "embed";
135
- case "FULL":
136
- return "full";
137
- default:
138
- throw new TypeError("readMode must be PUBLIC, EMBED, or FULL");
144
+ function validateScopes(scopes) {
145
+ if (!Array.isArray(scopes) || scopes.length === 0) {
146
+ throw new TypeError("scopes must include at least one DeckTokenScope");
147
+ }
148
+ for (const scope of scopes) {
149
+ validateRequiredString(scope, "scope");
139
150
  }
140
151
  }
152
+ function validateTokenTarget(input) {
153
+ if (input.deckId?.trim()) return;
154
+ if (input.publisherSlug?.trim() && input.gameKey?.trim() && input.externalSubject?.trim()) return;
155
+ throw new TypeError("deckId or publisherSlug, gameKey, and externalSubject are required");
156
+ }
141
157
  function normalizePrivateKey(privateKey) {
142
158
  const keyObject = privateKey instanceof crypto.KeyObject ? privateKey : typeof privateKey === "string" ? crypto.createPrivateKey(privateKey) : crypto.createPrivateKey({ key: Buffer.from(privateKey), format: "der", type: "pkcs8" });
143
159
  if (keyObject.type !== "private" || keyObject.asymmetricKeyType !== "ed25519") {
@@ -155,11 +171,11 @@ function decodeBase64URL(value) {
155
171
  return Buffer.from(value, "base64url");
156
172
  }
157
173
 
158
- exports.DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = DIRECT_DECK_ACCESS_TOKEN_AUDIENCE;
159
- exports.DIRECT_DECK_ACCESS_TOKEN_USE = DIRECT_DECK_ACCESS_TOKEN_USE;
174
+ exports.DIRECT_DECK_TOKEN_AUDIENCE = DIRECT_DECK_TOKEN_AUDIENCE;
175
+ exports.DIRECT_DECK_TOKEN_USE = DIRECT_DECK_TOKEN_USE;
160
176
  exports.MemoryCache = MemoryCache;
161
- exports.generateDirectDeckAccessKeyPair = generateDirectDeckAccessKeyPair;
162
- exports.signDirectDeckAccessToken = signDirectDeckAccessToken;
177
+ exports.generateDirectDeckTokenKeyPair = generateDirectDeckTokenKeyPair;
178
+ exports.signDirectDeckToken = signDirectDeckToken;
163
179
  Object.keys(client).forEach(function (k) {
164
180
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
165
181
  enumerable: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cache.ts","../src/deck-tokens.ts"],"names":["generateKeyPairSync","signBytes","KeyObject","createPrivateKey"],"mappings":";;;;;;AAiCO,IAAM,cAAN,MAAmC;AAAA,EAChC,KAAA,uBAAY,GAAA,EAAiC;AAAA;AAAA;AAAA;AAAA,EAKrD,IAAO,GAAA,EAAuB;AAC5B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAChC,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AAGnB,IAAA,IAAI,MAAM,SAAA,KAAc,IAAA,IAAQ,KAAK,GAAA,EAAI,GAAI,MAAM,SAAA,EAAW;AAC5D,MAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AACrB,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA,CAAM,KAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,GAAA,CAAO,GAAA,EAAa,KAAA,EAAU,GAAA,EAAoB;AAChD,IAAA,MAAM,YAAY,GAAA,KAAQ,MAAA,GAAY,KAAK,GAAA,EAAI,GAAI,MAAM,GAAA,GAAO,IAAA;AAChE,IAAA,IAAA,CAAK,MAAM,GAAA,CAAI,GAAA,EAAK,EAAE,KAAA,EAAO,WAAW,CAAA;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,GAAA,EAAmB;AACxB,IAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,GAAA,EAAsB;AACxB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,KAAM,IAAA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,KAAA,CAAM,IAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAA,GAAgB;AACd,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,IAAA,CAAK,KAAA,CAAM,SAAQ,EAAG;AAC/C,MAAA,IAAI,KAAA,CAAM,SAAA,KAAc,IAAA,IAAQ,GAAA,GAAM,MAAM,SAAA,EAAW;AACrD,QAAA,IAAA,CAAK,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AACF;ACtGO,IAAM,4BAAA,GAA+B;AACrC,IAAM,iCAAA,GAAoC;AAsB1C,SAAS,+BAAA,GAA2D;AAC1E,EAAA,MAAM,EAAE,UAAA,EAAY,SAAA,EAAU,GAAIA,2BAAoB,SAAS,CAAA;AAC/D,EAAA,MAAM,MAAM,SAAA,CAAU,MAAA,CAAO,EAAE,MAAA,EAAQ,OAAO,CAAA;AAC9C,EAAA,IAAI,EAAE,GAAA,IAAO,GAAA,CAAA,IAAQ,OAAO,GAAA,CAAI,MAAM,QAAA,EAAU;AAC/C,IAAA,MAAM,IAAI,UAAU,qCAAqC,CAAA;AAAA,EAC1D;AAEA,EAAA,OAAO;AAAA,IACN,aAAA,EAAe,UAAA,CAAW,MAAA,CAAO,EAAE,MAAA,EAAQ,OAAO,IAAA,EAAM,OAAA,EAAS,CAAA,CAAE,QAAA,EAAS;AAAA,IAC5E,eAAA,EAAiB,YAAA,CAAa,eAAA,CAAgB,GAAA,CAAI,CAAC,CAAC;AAAA,GACrD;AACD;AAEA,eAAsB,0BACrB,KAAA,EACkB;AAClB,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,oBAAY,IAAI,IAAA,EAAK;AAC5C,EAAA,sBAAA,CAAuB,KAAA,CAAM,UAAU,UAAU,CAAA;AACjD,EAAA,sBAAA,CAAuB,KAAA,CAAM,QAAQ,QAAQ,CAAA;AAC7C,EAAA,sBAAA,CAAuB,KAAA,CAAM,kBAAkB,kBAAkB,CAAA;AACjE,EAAA,sBAAA,CAAuB,KAAA,CAAM,OAAO,OAAO,CAAA;AAC3C,EAAA,IAAI,EAAE,oBAAoB,IAAA,CAAA,IAAS,MAAA,CAAO,MAAM,QAAA,CAAS,OAAA,EAAS,CAAA,EAAG;AACpE,IAAA,MAAM,IAAI,UAAU,+BAA+B,CAAA;AAAA,EACpD;AACA,EAAA,IAAI,EAAE,KAAA,CAAM,SAAA,YAAqB,IAAA,CAAA,IAAS,MAAA,CAAO,MAAM,KAAA,CAAM,SAAA,CAAU,OAAA,EAAS,CAAA,EAAG;AAClF,IAAA,MAAM,IAAI,UAAU,gCAAgC,CAAA;AAAA,EACrD;AACA,EAAA,IAAI,MAAM,SAAA,CAAU,OAAA,EAAQ,IAAK,QAAA,CAAS,SAAQ,EAAG;AACpD,IAAA,MAAM,IAAI,WAAW,kCAAkC,CAAA;AAAA,EACxD;AAEA,EAAA,MAAM,UAAA,GAAa,mBAAA,CAAoB,KAAA,CAAM,UAAU,CAAA;AACvD,EAAA,MAAM,MAAA,GAAS;AAAA,IACd,GAAA,EAAK,OAAA;AAAA,IACL,GAAA,EAAK,KAAA,CAAM,KAAA,CAAM,IAAA;AAAK,GACvB;AACA,EAAA,MAAM,OAAA,GAAU;AAAA,IACf,SAAA,EAAW,4BAAA;AAAA,IACX,OAAA,EAAS,KAAA,CAAM,MAAA,CAAO,IAAA,EAAK;AAAA,IAC3B,MAAA,EAAQ,KAAA,CAAM,gBAAA,CAAiB,IAAA,EAAK;AAAA,IACpC,SAAA,EAAW,iBAAA,CAAkB,KAAA,CAAM,QAAQ,CAAA;AAAA,IAC3C,GAAA,EAAK,KAAA,CAAM,QAAA,CAAS,IAAA,EAAK;AAAA,IACzB,GAAA,EAAK,iCAAA;AAAA,IACL,KAAK,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,OAAA,KAAY,GAAI,CAAA;AAAA,IACzC,KAAK,IAAA,CAAK,KAAA,CAAM,MAAM,SAAA,CAAU,OAAA,KAAY,GAAI;AAAA,GACjD;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,EAAS,IAAA,EAAK,EAAG;AAC1B,IAAA,OAAA,CAAQ,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAK;AAAA,EAClC;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,EAAS,IAAA,EAAK,EAAG;AAC1B,IAAA,OAAA,CAAQ,GAAA,GAAM,KAAA,CAAM,OAAA,CAAQ,IAAA,EAAK;AAAA,EAClC;AAEA,EAAA,MAAM,aAAA,GAAgB,eAAA,CAAgB,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AAC5D,EAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAC9D,EAAA,MAAM,YAAA,GAAe,CAAA,EAAG,aAAa,CAAA,CAAA,EAAI,cAAc,CAAA,CAAA;AACvD,EAAA,MAAM,YAAYC,WAAA,CAAU,IAAA,EAAM,OAAO,IAAA,CAAK,YAAY,GAAG,UAAU,CAAA;AAEvE,EAAA,OAAO,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,eAAA,CAAgB,SAAS,CAAC,CAAA,CAAA;AACrD;AAEA,SAAS,sBAAA,CAAuB,OAAe,IAAA,EAAoB;AAClE,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,IAAA,OAAW,EAAA,EAAI;AACrD,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,EAAG,IAAI,CAAA,YAAA,CAAc,CAAA;AAAA,EAC1C;AACD;AAEA,SAAS,kBAAkB,QAAA,EAA4C;AACtE,EAAA,QAAQ,QAAA;AAAU,IACjB,KAAK,QAAA;AACJ,MAAA,OAAO,QAAA;AAAA,IACR,KAAK,OAAA;AACJ,MAAA,OAAO,OAAA;AAAA,IACR,KAAK,MAAA;AACJ,MAAA,OAAO,MAAA;AAAA,IACR;AACC,MAAA,MAAM,IAAI,UAAU,yCAAyC,CAAA;AAAA;AAEhE;AAEA,SAAS,oBAAoB,UAAA,EAAwD;AACpF,EAAA,MAAM,SAAA,GACL,sBAAsBC,gBAAA,GACnB,UAAA,GACA,OAAO,UAAA,KAAe,QAAA,GACrBC,wBAAiB,UAAU,CAAA,GAC3BA,wBAAiB,EAAE,GAAA,EAAK,OAAO,IAAA,CAAK,UAAU,GAAG,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAM,OAAA,EAAS,CAAA;AACpF,EAAA,IAAI,SAAA,CAAU,IAAA,KAAS,SAAA,IAAa,SAAA,CAAU,sBAAsB,SAAA,EAAW;AAC9E,IAAA,MAAM,IAAI,UAAU,2CAA2C,CAAA;AAAA,EAChE;AACA,EAAA,OAAO,SAAA;AACR;AAEA,SAAS,gBAAgB,KAAA,EAAoC;AAC5D,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,SAAS,WAAW,CAAA;AAC/C;AAEA,SAAS,aAAa,KAAA,EAA2B;AAChD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,SAAS,QAAQ,CAAA;AAC5C;AAEA,SAAS,gBAAgB,KAAA,EAA2B;AACnD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,WAAW,CAAA;AACtC","file":"index.cjs","sourcesContent":["/**\n * In-memory cache implementation with TTL support for Node.js\n */\n\nimport type { Cache } from '@carddb/core'\n\ninterface CacheEntry<T> {\n value: T\n expiresAt: number | null\n}\n\n/**\n * Simple in-memory cache with TTL support.\n * Suitable for server-side applications where memory caching is appropriate.\n *\n * For production use with multiple instances, consider using Redis or another\n * distributed cache that implements the Cache interface.\n *\n * @example\n * import { CardDBClient, MemoryCache } from '@carddb/node'\n *\n * const client = new CardDBClient({\n * cache: new MemoryCache(),\n * cacheTtl: 300 // 5 minutes\n * })\n *\n * @example Manual usage\n * const cache = new MemoryCache()\n * cache.set('key', 'value', 60) // expires in 60 seconds\n * cache.get('key') // => 'value'\n * // After 60 seconds...\n * cache.get('key') // => null\n */\nexport class MemoryCache implements Cache {\n private store = new Map<string, CacheEntry<unknown>>()\n\n /**\n * Get a value from the cache\n */\n get<T>(key: string): T | null {\n const entry = this.store.get(key) as CacheEntry<T> | undefined\n if (!entry) return null\n\n // Check if expired\n if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n this.store.delete(key)\n return null\n }\n\n return entry.value\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - The cache key\n * @param value - The value to cache\n * @param ttl - Time to live in seconds (optional, undefined = no expiry)\n */\n set<T>(key: string, value: T, ttl?: number): void {\n const expiresAt = ttl !== undefined ? Date.now() + ttl * 1000 : null\n this.store.set(key, { value, expiresAt })\n }\n\n /**\n * Delete a value from the cache\n */\n delete(key: string): void {\n this.store.delete(key)\n }\n\n /**\n * Clear all cached values\n */\n clear(): void {\n this.store.clear()\n }\n\n /**\n * Check if a key exists and is not expired\n */\n has(key: string): boolean {\n return this.get(key) !== null\n }\n\n /**\n * Get the number of entries (including expired ones that haven't been cleaned up)\n */\n get size(): number {\n return this.store.size\n }\n\n /**\n * Clean up expired entries\n * Call this periodically if you want to reclaim memory from expired entries\n */\n cleanup(): void {\n const now = Date.now()\n for (const [key, entry] of this.store.entries()) {\n if (entry.expiresAt !== null && now > entry.expiresAt) {\n this.store.delete(key)\n }\n }\n }\n}\n","import { createPrivateKey, generateKeyPairSync, KeyObject, sign as signBytes } from 'node:crypto'\n\nexport const DIRECT_DECK_ACCESS_TOKEN_USE = 'carddb.deck_access.v1'\nexport const DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = 'carddb.deck_access'\n\nexport type DirectDeckAccessReadMode = 'PUBLIC' | 'EMBED' | 'FULL'\n\nexport interface SignDirectDeckAccessTokenInput {\n\tissuerId: string\n\tdeckId: string\n\tapiApplicationId: string\n\tkeyId: string\n\tprivateKey: KeyObject | string | Uint8Array\n\treadMode: DirectDeckAccessReadMode\n\tsubject?: string\n\ttokenId?: string\n\tissuedAt?: Date\n\texpiresAt: Date\n}\n\nexport interface DirectDeckAccessKeyPair {\n\tprivateKeyPem: string\n\tpublicKeyBase64: string\n}\n\nexport function generateDirectDeckAccessKeyPair(): DirectDeckAccessKeyPair {\n\tconst { privateKey, publicKey } = generateKeyPairSync('ed25519')\n\tconst jwk = publicKey.export({ format: 'jwk' })\n\tif (!('x' in jwk) || typeof jwk.x !== 'string') {\n\t\tthrow new TypeError('failed to export Ed25519 public key')\n\t}\n\n\treturn {\n\t\tprivateKeyPem: privateKey.export({ format: 'pem', type: 'pkcs8' }).toString(),\n\t\tpublicKeyBase64: encodeBase64(decodeBase64URL(jwk.x)),\n\t}\n}\n\nexport async function signDirectDeckAccessToken(\n\tinput: SignDirectDeckAccessTokenInput\n): Promise<string> {\n\tconst issuedAt = input.issuedAt ?? new Date()\n\tvalidateRequiredString(input.issuerId, 'issuerId')\n\tvalidateRequiredString(input.deckId, 'deckId')\n\tvalidateRequiredString(input.apiApplicationId, 'apiApplicationId')\n\tvalidateRequiredString(input.keyId, 'keyId')\n\tif (!(issuedAt instanceof Date) || Number.isNaN(issuedAt.getTime())) {\n\t\tthrow new TypeError('issuedAt must be a valid Date')\n\t}\n\tif (!(input.expiresAt instanceof Date) || Number.isNaN(input.expiresAt.getTime())) {\n\t\tthrow new TypeError('expiresAt must be a valid Date')\n\t}\n\tif (input.expiresAt.getTime() <= issuedAt.getTime()) {\n\t\tthrow new RangeError('expiresAt must be after issuedAt')\n\t}\n\n\tconst privateKey = normalizePrivateKey(input.privateKey)\n\tconst header = {\n\t\talg: 'EdDSA',\n\t\tkid: input.keyId.trim(),\n\t}\n\tconst payload = {\n\t\ttoken_use: DIRECT_DECK_ACCESS_TOKEN_USE,\n\t\tdeck_id: input.deckId.trim(),\n\t\tapp_id: input.apiApplicationId.trim(),\n\t\tread_mode: normalizeReadMode(input.readMode),\n\t\tiss: input.issuerId.trim(),\n\t\taud: DIRECT_DECK_ACCESS_TOKEN_AUDIENCE,\n\t\tiat: Math.floor(issuedAt.getTime() / 1000),\n\t\texp: Math.floor(input.expiresAt.getTime() / 1000),\n\t} as Record<string, string | number>\n\tif (input.subject?.trim()) {\n\t\tpayload.sub = input.subject.trim()\n\t}\n\tif (input.tokenId?.trim()) {\n\t\tpayload.jti = input.tokenId.trim()\n\t}\n\n\tconst encodedHeader = encodeBase64URL(JSON.stringify(header))\n\tconst encodedPayload = encodeBase64URL(JSON.stringify(payload))\n\tconst signingInput = `${encodedHeader}.${encodedPayload}`\n\tconst signature = signBytes(null, Buffer.from(signingInput), privateKey)\n\n\treturn `${signingInput}.${encodeBase64URL(signature)}`\n}\n\nfunction validateRequiredString(value: string, name: string): void {\n\tif (typeof value !== 'string' || value.trim() === '') {\n\t\tthrow new TypeError(`${name} is required`)\n\t}\n}\n\nfunction normalizeReadMode(readMode: DirectDeckAccessReadMode): string {\n\tswitch (readMode) {\n\t\tcase 'PUBLIC':\n\t\t\treturn 'public'\n\t\tcase 'EMBED':\n\t\t\treturn 'embed'\n\t\tcase 'FULL':\n\t\t\treturn 'full'\n\t\tdefault:\n\t\t\tthrow new TypeError('readMode must be PUBLIC, EMBED, or FULL')\n\t}\n}\n\nfunction normalizePrivateKey(privateKey: KeyObject | string | Uint8Array): KeyObject {\n\tconst keyObject =\n\t\tprivateKey instanceof KeyObject\n\t\t\t? privateKey\n\t\t\t: typeof privateKey === 'string'\n\t\t\t\t? createPrivateKey(privateKey)\n\t\t\t\t: createPrivateKey({ key: Buffer.from(privateKey), format: 'der', type: 'pkcs8' })\n\tif (keyObject.type !== 'private' || keyObject.asymmetricKeyType !== 'ed25519') {\n\t\tthrow new TypeError('privateKey must be an Ed25519 private key')\n\t}\n\treturn keyObject\n}\n\nfunction encodeBase64URL(value: string | Uint8Array): string {\n\treturn Buffer.from(value).toString('base64url')\n}\n\nfunction encodeBase64(value: Uint8Array): string {\n\treturn Buffer.from(value).toString('base64')\n}\n\nfunction decodeBase64URL(value: string): Uint8Array {\n\treturn Buffer.from(value, 'base64url')\n}\n"]}
1
+ {"version":3,"sources":["../src/cache.ts","../src/deck-tokens.ts"],"names":["generateKeyPairSync","signBytes","KeyObject","createPrivateKey"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * In-memory cache implementation with TTL support for Node.js\n */\n\nimport type { Cache } from '@carddb/core'\n\ninterface CacheEntry<T> {\n value: T\n expiresAt: number | null\n}\n\n/**\n * Simple in-memory cache with TTL support.\n * Suitable for server-side applications where memory caching is appropriate.\n *\n * For production use with multiple instances, consider using Redis or another\n * distributed cache that implements the Cache interface.\n *\n * @example\n * import { CardDBClient, MemoryCache } from '@carddb/node'\n *\n * const client = new CardDBClient({\n * cache: new MemoryCache(),\n * cacheTtl: 300 // 5 minutes\n * })\n *\n * @example Manual usage\n * const cache = new MemoryCache()\n * cache.set('key', 'value', 60) // expires in 60 seconds\n * cache.get('key') // => 'value'\n * // After 60 seconds...\n * cache.get('key') // => null\n */\nexport class MemoryCache implements Cache {\n private store = new Map<string, CacheEntry<unknown>>()\n\n /**\n * Get a value from the cache\n */\n get<T>(key: string): T | null {\n const entry = this.store.get(key) as CacheEntry<T> | undefined\n if (!entry) return null\n\n // Check if expired\n if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n this.store.delete(key)\n return null\n }\n\n return entry.value\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - The cache key\n * @param value - The value to cache\n * @param ttl - Time to live in seconds (optional, undefined = no expiry)\n */\n set<T>(key: string, value: T, ttl?: number): void {\n const expiresAt = ttl !== undefined ? Date.now() + ttl * 1000 : null\n this.store.set(key, { value, expiresAt })\n }\n\n /**\n * Delete a value from the cache\n */\n delete(key: string): void {\n this.store.delete(key)\n }\n\n /**\n * Clear all cached values\n */\n clear(): void {\n this.store.clear()\n }\n\n /**\n * Check if a key exists and is not expired\n */\n has(key: string): boolean {\n return this.get(key) !== null\n }\n\n /**\n * Get the number of entries (including expired ones that haven't been cleaned up)\n */\n get size(): number {\n return this.store.size\n }\n\n /**\n * Clean up expired entries\n * Call this periodically if you want to reclaim memory from expired entries\n */\n cleanup(): void {\n const now = Date.now()\n for (const [key, entry] of this.store.entries()) {\n if (entry.expiresAt !== null && now > entry.expiresAt) {\n this.store.delete(key)\n }\n }\n }\n}\n","import { createPrivateKey, generateKeyPairSync, KeyObject, sign as signBytes } from 'node:crypto'\nimport type { DeckRepresentation, DeckTokenScope } from '@carddb/core'\n\nexport const DIRECT_DECK_TOKEN_USE = 'carddb.deck_token.v1'\nexport const DIRECT_DECK_TOKEN_AUDIENCE = 'carddb.deck'\n\nexport interface SignDirectDeckTokenInput {\n\tissuerId: string\n\tapiApplicationId: string\n\tkeyId: string\n\tprivateKey: KeyObject | string | Uint8Array\n\tdeckId?: string\n\tpublisherSlug?: string\n\tgameKey?: string\n\texternalSubject?: string\n\tscopes: DeckTokenScope[]\n\trepresentation?: DeckRepresentation\n\tsubject?: string\n\ttokenId?: string\n\tissuedAt?: Date\n\texpiresAt: Date\n}\n\nexport interface DirectDeckTokenKeyPair {\n\tprivateKeyPem: string\n\tpublicKeyBase64: string\n}\n\nexport function generateDirectDeckTokenKeyPair(): DirectDeckTokenKeyPair {\n\tconst { privateKey, publicKey } = generateKeyPairSync('ed25519')\n\tconst jwk = publicKey.export({ format: 'jwk' })\n\tif (!('x' in jwk) || typeof jwk.x !== 'string') {\n\t\tthrow new TypeError('failed to export Ed25519 public key')\n\t}\n\n\treturn {\n\t\tprivateKeyPem: privateKey.export({ format: 'pem', type: 'pkcs8' }).toString(),\n\t\tpublicKeyBase64: encodeBase64(decodeBase64URL(jwk.x)),\n\t}\n}\n\nexport async function signDirectDeckToken(input: SignDirectDeckTokenInput): Promise<string> {\n\tconst issuedAt = input.issuedAt ?? new Date()\n\tvalidateRequiredString(input.issuerId, 'issuerId')\n\tvalidateRequiredString(input.apiApplicationId, 'apiApplicationId')\n\tvalidateRequiredString(input.keyId, 'keyId')\n\tvalidateScopes(input.scopes)\n\tvalidateTokenTarget(input)\n\tif (!(issuedAt instanceof Date) || Number.isNaN(issuedAt.getTime())) {\n\t\tthrow new TypeError('issuedAt must be a valid Date')\n\t}\n\tif (!(input.expiresAt instanceof Date) || Number.isNaN(input.expiresAt.getTime())) {\n\t\tthrow new TypeError('expiresAt must be a valid Date')\n\t}\n\tif (input.expiresAt.getTime() <= issuedAt.getTime()) {\n\t\tthrow new RangeError('expiresAt must be after issuedAt')\n\t}\n\n\tconst privateKey = normalizePrivateKey(input.privateKey)\n\tconst header = {\n\t\talg: 'EdDSA',\n\t\tkid: input.keyId.trim(),\n\t}\n\tconst payload = {\n\t\ttoken_use: DIRECT_DECK_TOKEN_USE,\n\t\tapp_id: input.apiApplicationId.trim(),\n\t\tscopes: input.scopes,\n\t\tiss: input.issuerId.trim(),\n\t\taud: DIRECT_DECK_TOKEN_AUDIENCE,\n\t\tiat: Math.floor(issuedAt.getTime() / 1000),\n\t\texp: Math.floor(input.expiresAt.getTime() / 1000),\n\t} as Record<string, string | number | string[]>\n\tif (input.deckId?.trim()) {\n\t\tpayload.deck_id = input.deckId.trim()\n\t}\n\tif (input.publisherSlug?.trim()) {\n\t\tpayload.publisher_slug = input.publisherSlug.trim()\n\t}\n\tif (input.gameKey?.trim()) {\n\t\tpayload.game_key = input.gameKey.trim()\n\t}\n\tif (input.externalSubject?.trim()) {\n\t\tpayload.external_subject = input.externalSubject.trim()\n\t}\n\tif (input.representation?.trim()) {\n\t\tpayload.representation = input.representation.trim()\n\t}\n\tif (input.subject?.trim()) {\n\t\tpayload.sub = input.subject.trim()\n\t}\n\tif (input.tokenId?.trim()) {\n\t\tpayload.jti = input.tokenId.trim()\n\t}\n\n\tconst encodedHeader = encodeBase64URL(JSON.stringify(header))\n\tconst encodedPayload = encodeBase64URL(JSON.stringify(payload))\n\tconst signingInput = `${encodedHeader}.${encodedPayload}`\n\tconst signature = signBytes(null, Buffer.from(signingInput), privateKey)\n\n\treturn `${signingInput}.${encodeBase64URL(signature)}`\n}\n\nfunction validateRequiredString(value: string, name: string): void {\n\tif (typeof value !== 'string' || value.trim() === '') {\n\t\tthrow new TypeError(`${name} is required`)\n\t}\n}\n\nfunction validateScopes(scopes: DeckTokenScope[]): void {\n\tif (!Array.isArray(scopes) || scopes.length === 0) {\n\t\tthrow new TypeError('scopes must include at least one DeckTokenScope')\n\t}\n\tfor (const scope of scopes) {\n\t\tvalidateRequiredString(scope, 'scope')\n\t}\n}\n\nfunction validateTokenTarget(input: SignDirectDeckTokenInput): void {\n\tif (input.deckId?.trim()) return\n\tif (input.publisherSlug?.trim() && input.gameKey?.trim() && input.externalSubject?.trim()) return\n\n\tthrow new TypeError('deckId or publisherSlug, gameKey, and externalSubject are required')\n}\n\nfunction normalizePrivateKey(privateKey: KeyObject | string | Uint8Array): KeyObject {\n\tconst keyObject =\n\t\tprivateKey instanceof KeyObject\n\t\t\t? privateKey\n\t\t\t: typeof privateKey === 'string'\n\t\t\t\t? createPrivateKey(privateKey)\n\t\t\t\t: createPrivateKey({ key: Buffer.from(privateKey), format: 'der', type: 'pkcs8' })\n\tif (keyObject.type !== 'private' || keyObject.asymmetricKeyType !== 'ed25519') {\n\t\tthrow new TypeError('privateKey must be an Ed25519 private key')\n\t}\n\treturn keyObject\n}\n\nfunction encodeBase64URL(value: string | Uint8Array): string {\n\treturn Buffer.from(value).toString('base64url')\n}\n\nfunction encodeBase64(value: Uint8Array): string {\n\treturn Buffer.from(value).toString('base64')\n}\n\nfunction decodeBase64URL(value: string): Uint8Array {\n\treturn Buffer.from(value, 'base64url')\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Cache } from '@carddb/core';
1
+ import { Cache, DeckTokenScope, DeckRepresentation } from '@carddb/core';
2
2
  import { KeyObject } from 'node:crypto';
3
3
  export * from '@carddb/client';
4
4
 
@@ -65,26 +65,29 @@ declare class MemoryCache implements Cache {
65
65
  cleanup(): void;
66
66
  }
67
67
 
68
- declare const DIRECT_DECK_ACCESS_TOKEN_USE = "carddb.deck_access.v1";
69
- declare const DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = "carddb.deck_access";
70
- type DirectDeckAccessReadMode = 'PUBLIC' | 'EMBED' | 'FULL';
71
- interface SignDirectDeckAccessTokenInput {
68
+ declare const DIRECT_DECK_TOKEN_USE = "carddb.deck_token.v1";
69
+ declare const DIRECT_DECK_TOKEN_AUDIENCE = "carddb.deck";
70
+ interface SignDirectDeckTokenInput {
72
71
  issuerId: string;
73
- deckId: string;
74
72
  apiApplicationId: string;
75
73
  keyId: string;
76
74
  privateKey: KeyObject | string | Uint8Array;
77
- readMode: DirectDeckAccessReadMode;
75
+ deckId?: string;
76
+ publisherSlug?: string;
77
+ gameKey?: string;
78
+ externalSubject?: string;
79
+ scopes: DeckTokenScope[];
80
+ representation?: DeckRepresentation;
78
81
  subject?: string;
79
82
  tokenId?: string;
80
83
  issuedAt?: Date;
81
84
  expiresAt: Date;
82
85
  }
83
- interface DirectDeckAccessKeyPair {
86
+ interface DirectDeckTokenKeyPair {
84
87
  privateKeyPem: string;
85
88
  publicKeyBase64: string;
86
89
  }
87
- declare function generateDirectDeckAccessKeyPair(): DirectDeckAccessKeyPair;
88
- declare function signDirectDeckAccessToken(input: SignDirectDeckAccessTokenInput): Promise<string>;
90
+ declare function generateDirectDeckTokenKeyPair(): DirectDeckTokenKeyPair;
91
+ declare function signDirectDeckToken(input: SignDirectDeckTokenInput): Promise<string>;
89
92
 
90
- export { DIRECT_DECK_ACCESS_TOKEN_AUDIENCE, DIRECT_DECK_ACCESS_TOKEN_USE, type DirectDeckAccessKeyPair, type DirectDeckAccessReadMode, MemoryCache, type SignDirectDeckAccessTokenInput, generateDirectDeckAccessKeyPair, signDirectDeckAccessToken };
93
+ export { DIRECT_DECK_TOKEN_AUDIENCE, DIRECT_DECK_TOKEN_USE, type DirectDeckTokenKeyPair, MemoryCache, type SignDirectDeckTokenInput, generateDirectDeckTokenKeyPair, signDirectDeckToken };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Cache } from '@carddb/core';
1
+ import { Cache, DeckTokenScope, DeckRepresentation } from '@carddb/core';
2
2
  import { KeyObject } from 'node:crypto';
3
3
  export * from '@carddb/client';
4
4
 
@@ -65,26 +65,29 @@ declare class MemoryCache implements Cache {
65
65
  cleanup(): void;
66
66
  }
67
67
 
68
- declare const DIRECT_DECK_ACCESS_TOKEN_USE = "carddb.deck_access.v1";
69
- declare const DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = "carddb.deck_access";
70
- type DirectDeckAccessReadMode = 'PUBLIC' | 'EMBED' | 'FULL';
71
- interface SignDirectDeckAccessTokenInput {
68
+ declare const DIRECT_DECK_TOKEN_USE = "carddb.deck_token.v1";
69
+ declare const DIRECT_DECK_TOKEN_AUDIENCE = "carddb.deck";
70
+ interface SignDirectDeckTokenInput {
72
71
  issuerId: string;
73
- deckId: string;
74
72
  apiApplicationId: string;
75
73
  keyId: string;
76
74
  privateKey: KeyObject | string | Uint8Array;
77
- readMode: DirectDeckAccessReadMode;
75
+ deckId?: string;
76
+ publisherSlug?: string;
77
+ gameKey?: string;
78
+ externalSubject?: string;
79
+ scopes: DeckTokenScope[];
80
+ representation?: DeckRepresentation;
78
81
  subject?: string;
79
82
  tokenId?: string;
80
83
  issuedAt?: Date;
81
84
  expiresAt: Date;
82
85
  }
83
- interface DirectDeckAccessKeyPair {
86
+ interface DirectDeckTokenKeyPair {
84
87
  privateKeyPem: string;
85
88
  publicKeyBase64: string;
86
89
  }
87
- declare function generateDirectDeckAccessKeyPair(): DirectDeckAccessKeyPair;
88
- declare function signDirectDeckAccessToken(input: SignDirectDeckAccessTokenInput): Promise<string>;
90
+ declare function generateDirectDeckTokenKeyPair(): DirectDeckTokenKeyPair;
91
+ declare function signDirectDeckToken(input: SignDirectDeckTokenInput): Promise<string>;
89
92
 
90
- export { DIRECT_DECK_ACCESS_TOKEN_AUDIENCE, DIRECT_DECK_ACCESS_TOKEN_USE, type DirectDeckAccessKeyPair, type DirectDeckAccessReadMode, MemoryCache, type SignDirectDeckAccessTokenInput, generateDirectDeckAccessKeyPair, signDirectDeckAccessToken };
93
+ export { DIRECT_DECK_TOKEN_AUDIENCE, DIRECT_DECK_TOKEN_USE, type DirectDeckTokenKeyPair, MemoryCache, type SignDirectDeckTokenInput, generateDirectDeckTokenKeyPair, signDirectDeckToken };
package/dist/index.js CHANGED
@@ -64,9 +64,9 @@ var MemoryCache = class {
64
64
  }
65
65
  }
66
66
  };
67
- var DIRECT_DECK_ACCESS_TOKEN_USE = "carddb.deck_access.v1";
68
- var DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = "carddb.deck_access";
69
- function generateDirectDeckAccessKeyPair() {
67
+ var DIRECT_DECK_TOKEN_USE = "carddb.deck_token.v1";
68
+ var DIRECT_DECK_TOKEN_AUDIENCE = "carddb.deck";
69
+ function generateDirectDeckTokenKeyPair() {
70
70
  const { privateKey, publicKey } = generateKeyPairSync("ed25519");
71
71
  const jwk = publicKey.export({ format: "jwk" });
72
72
  if (!("x" in jwk) || typeof jwk.x !== "string") {
@@ -77,12 +77,13 @@ function generateDirectDeckAccessKeyPair() {
77
77
  publicKeyBase64: encodeBase64(decodeBase64URL(jwk.x))
78
78
  };
79
79
  }
80
- async function signDirectDeckAccessToken(input) {
80
+ async function signDirectDeckToken(input) {
81
81
  const issuedAt = input.issuedAt ?? /* @__PURE__ */ new Date();
82
82
  validateRequiredString(input.issuerId, "issuerId");
83
- validateRequiredString(input.deckId, "deckId");
84
83
  validateRequiredString(input.apiApplicationId, "apiApplicationId");
85
84
  validateRequiredString(input.keyId, "keyId");
85
+ validateScopes(input.scopes);
86
+ validateTokenTarget(input);
86
87
  if (!(issuedAt instanceof Date) || Number.isNaN(issuedAt.getTime())) {
87
88
  throw new TypeError("issuedAt must be a valid Date");
88
89
  }
@@ -98,15 +99,29 @@ async function signDirectDeckAccessToken(input) {
98
99
  kid: input.keyId.trim()
99
100
  };
100
101
  const payload = {
101
- token_use: DIRECT_DECK_ACCESS_TOKEN_USE,
102
- deck_id: input.deckId.trim(),
102
+ token_use: DIRECT_DECK_TOKEN_USE,
103
103
  app_id: input.apiApplicationId.trim(),
104
- read_mode: normalizeReadMode(input.readMode),
104
+ scopes: input.scopes,
105
105
  iss: input.issuerId.trim(),
106
- aud: DIRECT_DECK_ACCESS_TOKEN_AUDIENCE,
106
+ aud: DIRECT_DECK_TOKEN_AUDIENCE,
107
107
  iat: Math.floor(issuedAt.getTime() / 1e3),
108
108
  exp: Math.floor(input.expiresAt.getTime() / 1e3)
109
109
  };
110
+ if (input.deckId?.trim()) {
111
+ payload.deck_id = input.deckId.trim();
112
+ }
113
+ if (input.publisherSlug?.trim()) {
114
+ payload.publisher_slug = input.publisherSlug.trim();
115
+ }
116
+ if (input.gameKey?.trim()) {
117
+ payload.game_key = input.gameKey.trim();
118
+ }
119
+ if (input.externalSubject?.trim()) {
120
+ payload.external_subject = input.externalSubject.trim();
121
+ }
122
+ if (input.representation?.trim()) {
123
+ payload.representation = input.representation.trim();
124
+ }
110
125
  if (input.subject?.trim()) {
111
126
  payload.sub = input.subject.trim();
112
127
  }
@@ -124,18 +139,19 @@ function validateRequiredString(value, name) {
124
139
  throw new TypeError(`${name} is required`);
125
140
  }
126
141
  }
127
- function normalizeReadMode(readMode) {
128
- switch (readMode) {
129
- case "PUBLIC":
130
- return "public";
131
- case "EMBED":
132
- return "embed";
133
- case "FULL":
134
- return "full";
135
- default:
136
- throw new TypeError("readMode must be PUBLIC, EMBED, or FULL");
142
+ function validateScopes(scopes) {
143
+ if (!Array.isArray(scopes) || scopes.length === 0) {
144
+ throw new TypeError("scopes must include at least one DeckTokenScope");
145
+ }
146
+ for (const scope of scopes) {
147
+ validateRequiredString(scope, "scope");
137
148
  }
138
149
  }
150
+ function validateTokenTarget(input) {
151
+ if (input.deckId?.trim()) return;
152
+ if (input.publisherSlug?.trim() && input.gameKey?.trim() && input.externalSubject?.trim()) return;
153
+ throw new TypeError("deckId or publisherSlug, gameKey, and externalSubject are required");
154
+ }
139
155
  function normalizePrivateKey(privateKey) {
140
156
  const keyObject = privateKey instanceof KeyObject ? privateKey : typeof privateKey === "string" ? createPrivateKey(privateKey) : createPrivateKey({ key: Buffer.from(privateKey), format: "der", type: "pkcs8" });
141
157
  if (keyObject.type !== "private" || keyObject.asymmetricKeyType !== "ed25519") {
@@ -153,6 +169,6 @@ function decodeBase64URL(value) {
153
169
  return Buffer.from(value, "base64url");
154
170
  }
155
171
 
156
- export { DIRECT_DECK_ACCESS_TOKEN_AUDIENCE, DIRECT_DECK_ACCESS_TOKEN_USE, MemoryCache, generateDirectDeckAccessKeyPair, signDirectDeckAccessToken };
172
+ export { DIRECT_DECK_TOKEN_AUDIENCE, DIRECT_DECK_TOKEN_USE, MemoryCache, generateDirectDeckTokenKeyPair, signDirectDeckToken };
157
173
  //# sourceMappingURL=index.js.map
158
174
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cache.ts","../src/deck-tokens.ts"],"names":["signBytes"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * In-memory cache implementation with TTL support for Node.js\n */\n\nimport type { Cache } from '@carddb/core'\n\ninterface CacheEntry<T> {\n value: T\n expiresAt: number | null\n}\n\n/**\n * Simple in-memory cache with TTL support.\n * Suitable for server-side applications where memory caching is appropriate.\n *\n * For production use with multiple instances, consider using Redis or another\n * distributed cache that implements the Cache interface.\n *\n * @example\n * import { CardDBClient, MemoryCache } from '@carddb/node'\n *\n * const client = new CardDBClient({\n * cache: new MemoryCache(),\n * cacheTtl: 300 // 5 minutes\n * })\n *\n * @example Manual usage\n * const cache = new MemoryCache()\n * cache.set('key', 'value', 60) // expires in 60 seconds\n * cache.get('key') // => 'value'\n * // After 60 seconds...\n * cache.get('key') // => null\n */\nexport class MemoryCache implements Cache {\n private store = new Map<string, CacheEntry<unknown>>()\n\n /**\n * Get a value from the cache\n */\n get<T>(key: string): T | null {\n const entry = this.store.get(key) as CacheEntry<T> | undefined\n if (!entry) return null\n\n // Check if expired\n if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n this.store.delete(key)\n return null\n }\n\n return entry.value\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - The cache key\n * @param value - The value to cache\n * @param ttl - Time to live in seconds (optional, undefined = no expiry)\n */\n set<T>(key: string, value: T, ttl?: number): void {\n const expiresAt = ttl !== undefined ? Date.now() + ttl * 1000 : null\n this.store.set(key, { value, expiresAt })\n }\n\n /**\n * Delete a value from the cache\n */\n delete(key: string): void {\n this.store.delete(key)\n }\n\n /**\n * Clear all cached values\n */\n clear(): void {\n this.store.clear()\n }\n\n /**\n * Check if a key exists and is not expired\n */\n has(key: string): boolean {\n return this.get(key) !== null\n }\n\n /**\n * Get the number of entries (including expired ones that haven't been cleaned up)\n */\n get size(): number {\n return this.store.size\n }\n\n /**\n * Clean up expired entries\n * Call this periodically if you want to reclaim memory from expired entries\n */\n cleanup(): void {\n const now = Date.now()\n for (const [key, entry] of this.store.entries()) {\n if (entry.expiresAt !== null && now > entry.expiresAt) {\n this.store.delete(key)\n }\n }\n }\n}\n","import { createPrivateKey, generateKeyPairSync, KeyObject, sign as signBytes } from 'node:crypto'\n\nexport const DIRECT_DECK_ACCESS_TOKEN_USE = 'carddb.deck_access.v1'\nexport const DIRECT_DECK_ACCESS_TOKEN_AUDIENCE = 'carddb.deck_access'\n\nexport type DirectDeckAccessReadMode = 'PUBLIC' | 'EMBED' | 'FULL'\n\nexport interface SignDirectDeckAccessTokenInput {\n\tissuerId: string\n\tdeckId: string\n\tapiApplicationId: string\n\tkeyId: string\n\tprivateKey: KeyObject | string | Uint8Array\n\treadMode: DirectDeckAccessReadMode\n\tsubject?: string\n\ttokenId?: string\n\tissuedAt?: Date\n\texpiresAt: Date\n}\n\nexport interface DirectDeckAccessKeyPair {\n\tprivateKeyPem: string\n\tpublicKeyBase64: string\n}\n\nexport function generateDirectDeckAccessKeyPair(): DirectDeckAccessKeyPair {\n\tconst { privateKey, publicKey } = generateKeyPairSync('ed25519')\n\tconst jwk = publicKey.export({ format: 'jwk' })\n\tif (!('x' in jwk) || typeof jwk.x !== 'string') {\n\t\tthrow new TypeError('failed to export Ed25519 public key')\n\t}\n\n\treturn {\n\t\tprivateKeyPem: privateKey.export({ format: 'pem', type: 'pkcs8' }).toString(),\n\t\tpublicKeyBase64: encodeBase64(decodeBase64URL(jwk.x)),\n\t}\n}\n\nexport async function signDirectDeckAccessToken(\n\tinput: SignDirectDeckAccessTokenInput\n): Promise<string> {\n\tconst issuedAt = input.issuedAt ?? new Date()\n\tvalidateRequiredString(input.issuerId, 'issuerId')\n\tvalidateRequiredString(input.deckId, 'deckId')\n\tvalidateRequiredString(input.apiApplicationId, 'apiApplicationId')\n\tvalidateRequiredString(input.keyId, 'keyId')\n\tif (!(issuedAt instanceof Date) || Number.isNaN(issuedAt.getTime())) {\n\t\tthrow new TypeError('issuedAt must be a valid Date')\n\t}\n\tif (!(input.expiresAt instanceof Date) || Number.isNaN(input.expiresAt.getTime())) {\n\t\tthrow new TypeError('expiresAt must be a valid Date')\n\t}\n\tif (input.expiresAt.getTime() <= issuedAt.getTime()) {\n\t\tthrow new RangeError('expiresAt must be after issuedAt')\n\t}\n\n\tconst privateKey = normalizePrivateKey(input.privateKey)\n\tconst header = {\n\t\talg: 'EdDSA',\n\t\tkid: input.keyId.trim(),\n\t}\n\tconst payload = {\n\t\ttoken_use: DIRECT_DECK_ACCESS_TOKEN_USE,\n\t\tdeck_id: input.deckId.trim(),\n\t\tapp_id: input.apiApplicationId.trim(),\n\t\tread_mode: normalizeReadMode(input.readMode),\n\t\tiss: input.issuerId.trim(),\n\t\taud: DIRECT_DECK_ACCESS_TOKEN_AUDIENCE,\n\t\tiat: Math.floor(issuedAt.getTime() / 1000),\n\t\texp: Math.floor(input.expiresAt.getTime() / 1000),\n\t} as Record<string, string | number>\n\tif (input.subject?.trim()) {\n\t\tpayload.sub = input.subject.trim()\n\t}\n\tif (input.tokenId?.trim()) {\n\t\tpayload.jti = input.tokenId.trim()\n\t}\n\n\tconst encodedHeader = encodeBase64URL(JSON.stringify(header))\n\tconst encodedPayload = encodeBase64URL(JSON.stringify(payload))\n\tconst signingInput = `${encodedHeader}.${encodedPayload}`\n\tconst signature = signBytes(null, Buffer.from(signingInput), privateKey)\n\n\treturn `${signingInput}.${encodeBase64URL(signature)}`\n}\n\nfunction validateRequiredString(value: string, name: string): void {\n\tif (typeof value !== 'string' || value.trim() === '') {\n\t\tthrow new TypeError(`${name} is required`)\n\t}\n}\n\nfunction normalizeReadMode(readMode: DirectDeckAccessReadMode): string {\n\tswitch (readMode) {\n\t\tcase 'PUBLIC':\n\t\t\treturn 'public'\n\t\tcase 'EMBED':\n\t\t\treturn 'embed'\n\t\tcase 'FULL':\n\t\t\treturn 'full'\n\t\tdefault:\n\t\t\tthrow new TypeError('readMode must be PUBLIC, EMBED, or FULL')\n\t}\n}\n\nfunction normalizePrivateKey(privateKey: KeyObject | string | Uint8Array): KeyObject {\n\tconst keyObject =\n\t\tprivateKey instanceof KeyObject\n\t\t\t? privateKey\n\t\t\t: typeof privateKey === 'string'\n\t\t\t\t? createPrivateKey(privateKey)\n\t\t\t\t: createPrivateKey({ key: Buffer.from(privateKey), format: 'der', type: 'pkcs8' })\n\tif (keyObject.type !== 'private' || keyObject.asymmetricKeyType !== 'ed25519') {\n\t\tthrow new TypeError('privateKey must be an Ed25519 private key')\n\t}\n\treturn keyObject\n}\n\nfunction encodeBase64URL(value: string | Uint8Array): string {\n\treturn Buffer.from(value).toString('base64url')\n}\n\nfunction encodeBase64(value: Uint8Array): string {\n\treturn Buffer.from(value).toString('base64')\n}\n\nfunction decodeBase64URL(value: string): Uint8Array {\n\treturn Buffer.from(value, 'base64url')\n}\n"]}
1
+ {"version":3,"sources":["../src/cache.ts","../src/deck-tokens.ts"],"names":["signBytes"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * In-memory cache implementation with TTL support for Node.js\n */\n\nimport type { Cache } from '@carddb/core'\n\ninterface CacheEntry<T> {\n value: T\n expiresAt: number | null\n}\n\n/**\n * Simple in-memory cache with TTL support.\n * Suitable for server-side applications where memory caching is appropriate.\n *\n * For production use with multiple instances, consider using Redis or another\n * distributed cache that implements the Cache interface.\n *\n * @example\n * import { CardDBClient, MemoryCache } from '@carddb/node'\n *\n * const client = new CardDBClient({\n * cache: new MemoryCache(),\n * cacheTtl: 300 // 5 minutes\n * })\n *\n * @example Manual usage\n * const cache = new MemoryCache()\n * cache.set('key', 'value', 60) // expires in 60 seconds\n * cache.get('key') // => 'value'\n * // After 60 seconds...\n * cache.get('key') // => null\n */\nexport class MemoryCache implements Cache {\n private store = new Map<string, CacheEntry<unknown>>()\n\n /**\n * Get a value from the cache\n */\n get<T>(key: string): T | null {\n const entry = this.store.get(key) as CacheEntry<T> | undefined\n if (!entry) return null\n\n // Check if expired\n if (entry.expiresAt !== null && Date.now() > entry.expiresAt) {\n this.store.delete(key)\n return null\n }\n\n return entry.value\n }\n\n /**\n * Set a value in the cache\n *\n * @param key - The cache key\n * @param value - The value to cache\n * @param ttl - Time to live in seconds (optional, undefined = no expiry)\n */\n set<T>(key: string, value: T, ttl?: number): void {\n const expiresAt = ttl !== undefined ? Date.now() + ttl * 1000 : null\n this.store.set(key, { value, expiresAt })\n }\n\n /**\n * Delete a value from the cache\n */\n delete(key: string): void {\n this.store.delete(key)\n }\n\n /**\n * Clear all cached values\n */\n clear(): void {\n this.store.clear()\n }\n\n /**\n * Check if a key exists and is not expired\n */\n has(key: string): boolean {\n return this.get(key) !== null\n }\n\n /**\n * Get the number of entries (including expired ones that haven't been cleaned up)\n */\n get size(): number {\n return this.store.size\n }\n\n /**\n * Clean up expired entries\n * Call this periodically if you want to reclaim memory from expired entries\n */\n cleanup(): void {\n const now = Date.now()\n for (const [key, entry] of this.store.entries()) {\n if (entry.expiresAt !== null && now > entry.expiresAt) {\n this.store.delete(key)\n }\n }\n }\n}\n","import { createPrivateKey, generateKeyPairSync, KeyObject, sign as signBytes } from 'node:crypto'\nimport type { DeckRepresentation, DeckTokenScope } from '@carddb/core'\n\nexport const DIRECT_DECK_TOKEN_USE = 'carddb.deck_token.v1'\nexport const DIRECT_DECK_TOKEN_AUDIENCE = 'carddb.deck'\n\nexport interface SignDirectDeckTokenInput {\n\tissuerId: string\n\tapiApplicationId: string\n\tkeyId: string\n\tprivateKey: KeyObject | string | Uint8Array\n\tdeckId?: string\n\tpublisherSlug?: string\n\tgameKey?: string\n\texternalSubject?: string\n\tscopes: DeckTokenScope[]\n\trepresentation?: DeckRepresentation\n\tsubject?: string\n\ttokenId?: string\n\tissuedAt?: Date\n\texpiresAt: Date\n}\n\nexport interface DirectDeckTokenKeyPair {\n\tprivateKeyPem: string\n\tpublicKeyBase64: string\n}\n\nexport function generateDirectDeckTokenKeyPair(): DirectDeckTokenKeyPair {\n\tconst { privateKey, publicKey } = generateKeyPairSync('ed25519')\n\tconst jwk = publicKey.export({ format: 'jwk' })\n\tif (!('x' in jwk) || typeof jwk.x !== 'string') {\n\t\tthrow new TypeError('failed to export Ed25519 public key')\n\t}\n\n\treturn {\n\t\tprivateKeyPem: privateKey.export({ format: 'pem', type: 'pkcs8' }).toString(),\n\t\tpublicKeyBase64: encodeBase64(decodeBase64URL(jwk.x)),\n\t}\n}\n\nexport async function signDirectDeckToken(input: SignDirectDeckTokenInput): Promise<string> {\n\tconst issuedAt = input.issuedAt ?? new Date()\n\tvalidateRequiredString(input.issuerId, 'issuerId')\n\tvalidateRequiredString(input.apiApplicationId, 'apiApplicationId')\n\tvalidateRequiredString(input.keyId, 'keyId')\n\tvalidateScopes(input.scopes)\n\tvalidateTokenTarget(input)\n\tif (!(issuedAt instanceof Date) || Number.isNaN(issuedAt.getTime())) {\n\t\tthrow new TypeError('issuedAt must be a valid Date')\n\t}\n\tif (!(input.expiresAt instanceof Date) || Number.isNaN(input.expiresAt.getTime())) {\n\t\tthrow new TypeError('expiresAt must be a valid Date')\n\t}\n\tif (input.expiresAt.getTime() <= issuedAt.getTime()) {\n\t\tthrow new RangeError('expiresAt must be after issuedAt')\n\t}\n\n\tconst privateKey = normalizePrivateKey(input.privateKey)\n\tconst header = {\n\t\talg: 'EdDSA',\n\t\tkid: input.keyId.trim(),\n\t}\n\tconst payload = {\n\t\ttoken_use: DIRECT_DECK_TOKEN_USE,\n\t\tapp_id: input.apiApplicationId.trim(),\n\t\tscopes: input.scopes,\n\t\tiss: input.issuerId.trim(),\n\t\taud: DIRECT_DECK_TOKEN_AUDIENCE,\n\t\tiat: Math.floor(issuedAt.getTime() / 1000),\n\t\texp: Math.floor(input.expiresAt.getTime() / 1000),\n\t} as Record<string, string | number | string[]>\n\tif (input.deckId?.trim()) {\n\t\tpayload.deck_id = input.deckId.trim()\n\t}\n\tif (input.publisherSlug?.trim()) {\n\t\tpayload.publisher_slug = input.publisherSlug.trim()\n\t}\n\tif (input.gameKey?.trim()) {\n\t\tpayload.game_key = input.gameKey.trim()\n\t}\n\tif (input.externalSubject?.trim()) {\n\t\tpayload.external_subject = input.externalSubject.trim()\n\t}\n\tif (input.representation?.trim()) {\n\t\tpayload.representation = input.representation.trim()\n\t}\n\tif (input.subject?.trim()) {\n\t\tpayload.sub = input.subject.trim()\n\t}\n\tif (input.tokenId?.trim()) {\n\t\tpayload.jti = input.tokenId.trim()\n\t}\n\n\tconst encodedHeader = encodeBase64URL(JSON.stringify(header))\n\tconst encodedPayload = encodeBase64URL(JSON.stringify(payload))\n\tconst signingInput = `${encodedHeader}.${encodedPayload}`\n\tconst signature = signBytes(null, Buffer.from(signingInput), privateKey)\n\n\treturn `${signingInput}.${encodeBase64URL(signature)}`\n}\n\nfunction validateRequiredString(value: string, name: string): void {\n\tif (typeof value !== 'string' || value.trim() === '') {\n\t\tthrow new TypeError(`${name} is required`)\n\t}\n}\n\nfunction validateScopes(scopes: DeckTokenScope[]): void {\n\tif (!Array.isArray(scopes) || scopes.length === 0) {\n\t\tthrow new TypeError('scopes must include at least one DeckTokenScope')\n\t}\n\tfor (const scope of scopes) {\n\t\tvalidateRequiredString(scope, 'scope')\n\t}\n}\n\nfunction validateTokenTarget(input: SignDirectDeckTokenInput): void {\n\tif (input.deckId?.trim()) return\n\tif (input.publisherSlug?.trim() && input.gameKey?.trim() && input.externalSubject?.trim()) return\n\n\tthrow new TypeError('deckId or publisherSlug, gameKey, and externalSubject are required')\n}\n\nfunction normalizePrivateKey(privateKey: KeyObject | string | Uint8Array): KeyObject {\n\tconst keyObject =\n\t\tprivateKey instanceof KeyObject\n\t\t\t? privateKey\n\t\t\t: typeof privateKey === 'string'\n\t\t\t\t? createPrivateKey(privateKey)\n\t\t\t\t: createPrivateKey({ key: Buffer.from(privateKey), format: 'der', type: 'pkcs8' })\n\tif (keyObject.type !== 'private' || keyObject.asymmetricKeyType !== 'ed25519') {\n\t\tthrow new TypeError('privateKey must be an Ed25519 private key')\n\t}\n\treturn keyObject\n}\n\nfunction encodeBase64URL(value: string | Uint8Array): string {\n\treturn Buffer.from(value).toString('base64url')\n}\n\nfunction encodeBase64(value: Uint8Array): string {\n\treturn Buffer.from(value).toString('base64')\n}\n\nfunction decodeBase64URL(value: string): Uint8Array {\n\treturn Buffer.from(value, 'base64url')\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carddb/node",
3
- "version": "0.4.2",
3
+ "version": "0.4.6",
4
4
  "description": "CardDB client for Node.js, Bun, and Deno",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -50,8 +50,8 @@
50
50
  "url": "https://github.com/carddb/carddb-js/issues"
51
51
  },
52
52
  "dependencies": {
53
- "@carddb/client": "0.4.2",
54
- "@carddb/core": "0.4.2"
53
+ "@carddb/client": "0.4.6",
54
+ "@carddb/core": "0.4.6"
55
55
  },
56
56
  "engines": {
57
57
  "node": ">=18.0.0"