@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 +16 -9
- package/dist/index.cjs +39 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -11
- package/dist/index.d.ts +14 -11
- package/dist/index.js +36 -20
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
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.
|
|
57
|
+
const exchange = await client.decks.exchangeToken({
|
|
56
58
|
deckId: upsert.deck.id,
|
|
57
|
-
|
|
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
|
|
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
|
-
|
|
69
|
-
|
|
74
|
+
generateDirectDeckTokenKeyPair,
|
|
75
|
+
signDirectDeckToken,
|
|
70
76
|
} from '@carddb/node'
|
|
71
77
|
|
|
72
|
-
const keyPair =
|
|
78
|
+
const keyPair = generateDirectDeckTokenKeyPair()
|
|
73
79
|
|
|
74
80
|
console.log(keyPair.publicKeyBase64) // Register this on directSigningKey.publicKey
|
|
75
81
|
|
|
76
|
-
const token = await
|
|
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
|
-
|
|
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
|
|
70
|
-
var
|
|
71
|
-
function
|
|
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
|
|
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:
|
|
104
|
-
deck_id: input.deckId.trim(),
|
|
104
|
+
token_use: DIRECT_DECK_TOKEN_USE,
|
|
105
105
|
app_id: input.apiApplicationId.trim(),
|
|
106
|
-
|
|
106
|
+
scopes: input.scopes,
|
|
107
107
|
iss: input.issuerId.trim(),
|
|
108
|
-
aud:
|
|
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
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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.
|
|
159
|
-
exports.
|
|
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.
|
|
162
|
-
exports.
|
|
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,
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
|
69
|
-
declare const
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
86
|
+
interface DirectDeckTokenKeyPair {
|
|
84
87
|
privateKeyPem: string;
|
|
85
88
|
publicKeyBase64: string;
|
|
86
89
|
}
|
|
87
|
-
declare function
|
|
88
|
-
declare function
|
|
90
|
+
declare function generateDirectDeckTokenKeyPair(): DirectDeckTokenKeyPair;
|
|
91
|
+
declare function signDirectDeckToken(input: SignDirectDeckTokenInput): Promise<string>;
|
|
89
92
|
|
|
90
|
-
export {
|
|
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
|
|
69
|
-
declare const
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
86
|
+
interface DirectDeckTokenKeyPair {
|
|
84
87
|
privateKeyPem: string;
|
|
85
88
|
publicKeyBase64: string;
|
|
86
89
|
}
|
|
87
|
-
declare function
|
|
88
|
-
declare function
|
|
90
|
+
declare function generateDirectDeckTokenKeyPair(): DirectDeckTokenKeyPair;
|
|
91
|
+
declare function signDirectDeckToken(input: SignDirectDeckTokenInput): Promise<string>;
|
|
89
92
|
|
|
90
|
-
export {
|
|
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
|
|
68
|
-
var
|
|
69
|
-
function
|
|
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
|
|
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:
|
|
102
|
-
deck_id: input.deckId.trim(),
|
|
102
|
+
token_use: DIRECT_DECK_TOKEN_USE,
|
|
103
103
|
app_id: input.apiApplicationId.trim(),
|
|
104
|
-
|
|
104
|
+
scopes: input.scopes,
|
|
105
105
|
iss: input.issuerId.trim(),
|
|
106
|
-
aud:
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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 {
|
|
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.
|
|
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.
|
|
54
|
-
"@carddb/core": "0.4.
|
|
53
|
+
"@carddb/client": "0.4.6",
|
|
54
|
+
"@carddb/core": "0.4.6"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|
|
57
57
|
"node": ">=18.0.0"
|