@agnostack/next-shopify 4.0.0-canary.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,101 @@
1
+ # [4.0.0](https://github.com/agnostack/next-shopify/compare/v3.3.0...v4.0.0) (2026-10-09)
2
+
3
+
4
+ * feat!: require a storage implementation rather than bundling one ([a13f021](https://github.com/agnostack/next-shopify/commit/a13f02143b0836a35e0506443ec9fdbf674518db))
5
+
6
+
7
+ ### Bug Fixes
8
+
9
+ * add dev copies of the required peers so checkJs can resolve them ([6c7fdd2](https://github.com/agnostack/next-shopify/commit/6c7fdd2ce0f6094bc0477b48cea39be189bc1329))
10
+ * add pnpm-lock.yaml ([bfc803a](https://github.com/agnostack/next-shopify/commit/bfc803a3e758d72e13a1f201cff266f43a28665d))
11
+ * address the review notes on the checkjs stack ([52dca8d](https://github.com/agnostack/next-shopify/commit/52dca8d006f6d7f192982d5a91c5b814041127ec))
12
+ * align .gitignore with the shared template ([5968bf1](https://github.com/agnostack/next-shopify/commit/5968bf193a32cd0c4f3f45f6d71694b8f872961c))
13
+ * build and test against firebase-admin 14.4.0 ([cf75069](https://github.com/agnostack/next-shopify/commit/cf75069fab599ab29d8e2d95068c91044c7ae191))
14
+ * bump cookies to 0.9.1 ([6e7554c](https://github.com/agnostack/next-shopify/commit/6e7554c3bd85bc57f9bffcafecd7c65564d06160))
15
+ * bump isbot to 5.2.2 and import it by name ([30c1228](https://github.com/agnostack/next-shopify/commit/30c12288f1754809d25a4c9db2334d09c487db08))
16
+ * bump js-base64 to 3.9.3 ([7543e32](https://github.com/agnostack/next-shopify/commit/7543e3220f5b9d265a1ee205caff08d0ef0cf1ae))
17
+ * bump jwt-decode to 4.0.0 ([bc5df03](https://github.com/agnostack/next-shopify/commit/bc5df03761dc691566833ea9fa5e1e93622befb1))
18
+ * bump minimatch to 10.2.6 ([8aa0dc6](https://github.com/agnostack/next-shopify/commit/8aa0dc61678a778522409e2659baa490728d8e2b))
19
+ * bump nextjs-cors to 2.2.1 ([173860d](https://github.com/agnostack/next-shopify/commit/173860dd166f8e7eaade280f5480dcd207f637a6))
20
+ * bump uuid to 11.1.1 and drop its stub typings ([fa02d50](https://github.com/agnostack/next-shopify/commit/fa02d50b52540577a7191fb9b5e02dcba63213c9))
21
+ * cap the firebase-admin peer below 14 ([42b2b32](https://github.com/agnostack/next-shopify/commit/42b2b3259184e623418209704bfd82c86a56481a)), closes [firebase/firebase-admin-node#3164](https://github.com/firebase/firebase-admin-node/issues/3164)
22
+ * compare ArrayBuffer hashes by their byte length ([1b08a62](https://github.com/agnostack/next-shopify/commit/1b08a623fceea251b1721c39fa9907c77b22bb7f))
23
+ * compile the CommonJS sources for the node that can require esm ([f30407a](https://github.com/agnostack/next-shopify/commit/f30407a77e4344d9d6a410b921a9b17edfc891c5))
24
+ * declare typescript as 5.x like the rest of the toolchain ([a1e7ee3](https://github.com/agnostack/next-shopify/commit/a1e7ee38f8bd189265f1590811236ddb29b839af))
25
+ * decode zstd in the proxy response adapter and fail closed on unknown encodings ([cdcd8a5](https://github.com/agnostack/next-shopify/commit/cdcd8a559e9ff772bdcd4e42ce46a3ebf6fca92c))
26
+ * drop jsonwebtoken ([5abb88c](https://github.com/agnostack/next-shopify/commit/5abb88c466e967134ca785c005067566150d4965))
27
+ * fail clearly when the shopify api is bypassed instead of with a TypeError ([d7e647e](https://github.com/agnostack/next-shopify/commit/d7e647e0fd75ffb4e5a09b203768720f6460b153))
28
+ * float firebase-admin to 14.x ([5784493](https://github.com/agnostack/next-shopify/commit/5784493cdbcb3130f54299b364d61f29b0179bdb))
29
+ * float the http-proxy devDependency to 1.18.x ([825819b](https://github.com/agnostack/next-shopify/commit/825819b0e39e8d3b4ef62fec9dfc8ff8a90a924b))
30
+ * give the dual export subpaths a default condition ([10c2c90](https://github.com/agnostack/next-shopify/commit/10c2c9024ccab238444249551d32aeda9520be21))
31
+ * give the test workflow read-only permissions and cancel superseded runs ([2b26326](https://github.com/agnostack/next-shopify/commit/2b26326d78b6bc7c02eac948009e24191dd6274f))
32
+ * hand isbot a string for the User-Agent lookup ([2b42b4c](https://github.com/agnostack/next-shopify/commit/2b42b4c588af3ed0358904e3fffeed2e8401a010))
33
+ * import jsonwebtoken as a default so the esm build links under native node ([3db30d9](https://github.com/agnostack/next-shopify/commit/3db30d9b9d8a6882de7ff714e93b2fc3df3eece2))
34
+ * inflate proxied responses in the adapter so corrupt bodies fail closed ([3056b5d](https://github.com/agnostack/next-shopify/commit/3056b5df9a351368725111feeed7d7371e652e56))
35
+ * keep the storage contract and its implementations in one tree ([2b380e7](https://github.com/agnostack/next-shopify/commit/2b380e789d41385d56f2c4464917db32e888beb1))
36
+ * keep the storage contract in shared and the storage folder for implementations ([e862a11](https://github.com/agnostack/next-shopify/commit/e862a1129b71668fcae431b164ba66e1e51906e7))
37
+ * mark the version-signalling peers optional ([87b2c6d](https://github.com/agnostack/next-shopify/commit/87b2c6de6d7cdd89b9a9b60be56c7955fa85e3df))
38
+ * move both workflows to checkout v6, pnpm/action-setup v6 and setup-node v5 ([46669e7](https://github.com/agnostack/next-shopify/commit/46669e73fd17862726970d622c232486df5eaac5))
39
+ * move the session handler to the modular firebase-admin imports ([0ef15fc](https://github.com/agnostack/next-shopify/commit/0ef15fc7a26a8a6989ca0ec5831d762504e5a89f))
40
+ * move vitest to 4.x ([2b0b7de](https://github.com/agnostack/next-shopify/commit/2b0b7de7b70120cda4b1051a89a1848137e1e377))
41
+ * name getShopifyHelpers' return type so its declaration is emitted ([e871d98](https://github.com/agnostack/next-shopify/commit/e871d987d0ad93d8c8e9442a512067e2ba531bb3))
42
+ * notes and a GitHub release on every publishing branch, changelog only on stable releases ([2c9ad23](https://github.com/agnostack/next-shopify/commit/2c9ad2356a0c3f834bac8423d900078513d0eca0))
43
+ * queue overlapping release runs instead of cancelling them ([d75f9dc](https://github.com/agnostack/next-shopify/commit/d75f9dc0c246e3388b28ef4c4a1429140e8248dd))
44
+ * raise the next and react peer floors to the versions the app runs ([1efb9c9](https://github.com/agnostack/next-shopify/commit/1efb9c95a2c41e6b97a89e1c76610de4a3df7039))
45
+ * read the proxied content-encoding case-insensitively and treat identity as none ([d2e0351](https://github.com/agnostack/next-shopify/commit/d2e03513e93212a95f2cb1c0b6d3173285f5dd0a))
46
+ * reflow the checkJs JSDoc into multi-line blocks and drop the inline parameter casts ([73e855e](https://github.com/agnostack/next-shopify/commit/73e855ea290b13b6eccee5afbd066269f077dcff))
47
+ * replace node-http-proxy-json with a typed responseInterceptor adapter ([45bd502](https://github.com/agnostack/next-shopify/commit/45bd502bf98fb1ddfe057d05e35f6deae123143c))
48
+ * reset the proxied response immediately when the upstream aborts mid-body ([c14225e](https://github.com/agnostack/next-shopify/commit/c14225e10767c1781617d8e4e7d499b2e63d8ab7))
49
+ * resolve modules with node16 and split the tsconfigs ([9b87dd2](https://github.com/agnostack/next-shopify/commit/9b87dd2316c16f0c3272f4b1b57e72233b599e52))
50
+ * run semantic-release through pnpm exec so package keywords are not masked ([39cd371](https://github.com/agnostack/next-shopify/commit/39cd371007223b2bb1bda10c99a907f8ad91b486))
51
+ * satisfy the typecheck and pin the stored document paths ([83b2691](https://github.com/agnostack/next-shopify/commit/83b2691ddaf0b2e5f6b4fe0309a12bd6c56115ee))
52
+ * sign extension session tokens with jose ([0c3b0c0](https://github.com/agnostack/next-shopify/commit/0c3b0c00e017a42d25867032a78c18ada7881d15))
53
+ * skip CI on the release commit ([c277847](https://github.com/agnostack/next-shopify/commit/c277847f20d97eef849a9438945727e898d964e1))
54
+ * standardize version ([6fe44c5](https://github.com/agnostack/next-shopify/commit/6fe44c5ead19be5740ecd6ca58ddad82426f8ed3))
55
+ * standardize version ([4218665](https://github.com/agnostack/next-shopify/commit/42186651b7604a8438a4a01fedb940f5efae4f73))
56
+ * suffix relative imports so the esm build links under native node ([c73827e](https://github.com/agnostack/next-shopify/commit/c73827eea42044c980d11330abe3a5ee6a3a9893))
57
+ * take unknown in ensureStorage, since that is what it is deciding ([5a8a3f8](https://github.com/agnostack/next-shopify/commit/5a8a3f88fd18c5654e0928ab9c69626cd7ec2356))
58
+ * throw a clear error when CryptoHandler is used before it is initialized ([db17f06](https://github.com/agnostack/next-shopify/commit/db17f06ccc489feaa0af7cd5347f2f96cd09d078))
59
+ * type state as the consumer's own data rather than as opaque ([ca13e28](https://github.com/agnostack/next-shopify/commit/ca13e28f1e7f91a8c39c74f516e998c7848f8d0f))
60
+ * type the public helpers surface so it stops discarding the contract ([77088dd](https://github.com/agnostack/next-shopify/commit/77088dd4718a7de119c9446ba52df8648f47a388))
61
+ * type the storage contract so an implementation cannot read what it stores ([06c05bf](https://github.com/agnostack/next-shopify/commit/06c05bf80af1d5b1c2cc3fa8c4d0195e533daf33))
62
+ * type-check the js sources ([61f0783](https://github.com/agnostack/next-shopify/commit/61f0783e7e0108af8304b80a9d6391856afa40c3))
63
+ * verify extension session tokens with jose ([b436d11](https://github.com/agnostack/next-shopify/commit/b436d11b75f09470a98667130937a69753531ed4))
64
+
65
+
66
+ ### chore
67
+
68
+ * modernize the toolchain to node 22, pnpm, eslint 9 and vitest ([0cc5cd4](https://github.com/agnostack/next-shopify/commit/0cc5cd4648ae2d173d192018ae204836b064bac8))
69
+
70
+
71
+ ### Features
72
+
73
+ * add ESM output and /esm + /shared subpath exports ([52c7c15](https://github.com/agnostack/next-shopify/commit/52c7c1530eaee759c3a02adb663f3d68d6bffe47))
74
+ * declare a session-storage contract and accept an implementation ([9f22481](https://github.com/agnostack/next-shopify/commit/9f22481f4cc65e6edfe908a0505a7daecab75ea3))
75
+ * enable npm trusted publishing with the token as fallback ([e72f5d2](https://github.com/agnostack/next-shopify/commit/e72f5d241e38569814b870c10fb0e499ff82baac))
76
+ * preview the release a labelled PR would cut ([746674c](https://github.com/agnostack/next-shopify/commit/746674c46d52136e706d7559c6daa5334eb6d88b)), closes [#67](https://github.com/agnostack/next-shopify/issues/67)
77
+ * replaced getAPIConfig with API_CONFIG page config literal ([d452ee9](https://github.com/agnostack/next-shopify/commit/d452ee90232a392fc4056ca11451a49a35736e00))
78
+ * thread the storage contract through every handler factory ([e3dfa32](https://github.com/agnostack/next-shopify/commit/e3dfa32a00358bb5bb78236f0d96dec1acebcd0a)), closes [#204](https://github.com/agnostack/next-shopify/issues/204)
79
+
80
+
81
+ ### BREAKING CHANGES
82
+
83
+ * getShopifyHelpers no longer falls back to a bundled firestore
84
+ store. Every consumer must pass `storage`; omitting it now throws at composition
85
+ instead of silently writing sessions to a store this package chose. The
86
+ ./storage/firestore export and the firebase-admin peer are removed with it.
87
+
88
+ Also recorded here because 4.0.0-canary.9 shipped it under a `fix:` type: the
89
+ ./storage export was retired in favour of ./shared, which carries the contract,
90
+ its types and ensureStorage.
91
+
92
+ Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93
+ * requires node >=22.
94
+
95
+ Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
96
+ * getAPIConfig has been removed — calling it in `export const config` is a fatal build error under next 15's static analyzer. Use the named re-export:
97
+ `export { API_CONFIG as config } from '@agnostack/next-shopify/lib'`
98
+
1
99
  # [3.3.0](https://github.com/agnostack/next-shopify/compare/v3.2.2...v3.3.0) (2025-08-20)
2
100
 
3
101
 
@@ -74,37 +74,26 @@ const prepareSessionPayload = (session) => {
74
74
  const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
75
75
  return Object.assign({ onlineAccessInfo }, sessionData);
76
76
  };
77
- // NOTE: imported lazily so an app that supplies its own storage never loads firebase-admin,
78
- // even though it is still the default. PR 4 removes this branch and the peer with it.
79
- /**
80
- * @param {any} firebaseConfig
81
- * @param {any} options
82
- */
83
- const createDefaultStorage = async (firebaseConfig, options) => {
84
- const { createFirestoreStorage } = await import('../../storage/firestore.js');
85
- return createFirestoreStorage(firebaseConfig, options);
86
- };
87
77
  // TODO: move to curry??
88
78
  /**
89
79
  * @param {any} [serverRuntimeConfig]
90
80
  * @param {any} [config]
91
- * @param {import('../../shared/index.js').StorageOptions} [options]
81
+ * @param {import('../../shared/index.js').StorageOptions} options
82
+ * ⚠️ `storage` is REQUIRED. There is no bundled implementation to fall back to, so omitting it
83
+ * throws here rather than quietly writing sessions somewhere this app does not own.
92
84
  * @returns {Promise<import('./types.js').ShopifyHelpers>}
93
85
  */
94
86
  export const getShopifyHelpers = async (serverRuntimeConfig, config, { storage: _storage } = {}) => {
95
87
  var _a, _b;
96
88
  const shopify = getInitializedShopify(serverRuntimeConfig);
97
- const { APP_ID, FB_CONFIG, FB_ROOT_COLLECTION, SHOPIFY_RUN_MODE, SESSION_CONFIG, PAYMENT_CONFIG, } = serverRuntimeConfig;
89
+ const { SHOPIFY_RUN_MODE, SESSION_CONFIG, PAYMENT_CONFIG, } = serverRuntimeConfig;
98
90
  const { shop } = config !== null && config !== void 0 ? config : {};
99
91
  const isReviewMode = isTrue((SHOPIFY_RUN_MODE === 'review') &&
100
92
  (PAYMENT_CONFIG === null || PAYMENT_CONFIG === void 0 ? void 0 : PAYMENT_CONFIG.required));
101
93
  // TODO: should this use our helper instead (and/or as a fallback)??
102
94
  const shopId = (_b = (_a = shopify === null || shopify === void 0 ? void 0 : shopify.utils) === null || _a === void 0 ? void 0 : _a.sanitizeShop) === null || _b === void 0 ? void 0 : _b.call(_a, shop);
103
95
  const cryptoHandler = CryptoHandler.getInstance(SESSION_CONFIG);
104
- const storage = ensureStorage(_storage !== null && _storage !== void 0 ? _storage : await createDefaultStorage(FB_CONFIG, {
105
- collection: FB_ROOT_COLLECTION,
106
- configurationId: APP_ID,
107
- }));
96
+ const storage = ensureStorage(_storage);
108
97
  // NOTE: this should handle running in next to test non-shopify functionality
109
98
  if (!shopify) {
110
99
  return {
@@ -21,6 +21,11 @@ export const STORAGE_METHODS = [
21
21
  */
22
22
  export const ensureStorage = (storage) => {
23
23
  const candidate = /** @type {Record<string, unknown> | null | undefined} */ (storage);
24
+ // ⚠️ the upgrade path, called out separately: this package used to bundle a firestore store and
25
+ // fall back to it. Listing all seven methods as missing would be true and would not explain why.
26
+ if (!candidate) {
27
+ throw new Error('No session storage supplied. Pass one as `storage` - this package no longer bundles a default.');
28
+ }
24
29
  const missing = ensureArray(STORAGE_METHODS).filter((method) => (typeof (candidate === null || candidate === void 0 ? void 0 : candidate[method]) !== 'function'));
25
30
  if (missing.length) {
26
31
  throw new Error(`Invalid session storage, missing: ${missing.join(', ')}`);
@@ -78,37 +78,26 @@ const prepareSessionPayload = (session) => {
78
78
  const _b = _onlineAccessInfo !== null && _onlineAccessInfo !== void 0 ? _onlineAccessInfo : {}, { associated_user } = _b, onlineAccessInfo = __rest(_b, ["associated_user"]);
79
79
  return Object.assign({ onlineAccessInfo }, sessionData);
80
80
  };
81
- // NOTE: imported lazily so an app that supplies its own storage never loads firebase-admin,
82
- // even though it is still the default. PR 4 removes this branch and the peer with it.
83
- /**
84
- * @param {any} firebaseConfig
85
- * @param {any} options
86
- */
87
- const createDefaultStorage = async (firebaseConfig, options) => {
88
- const { createFirestoreStorage } = await import('../../storage/firestore.js');
89
- return createFirestoreStorage(firebaseConfig, options);
90
- };
91
81
  // TODO: move to curry??
92
82
  /**
93
83
  * @param {any} [serverRuntimeConfig]
94
84
  * @param {any} [config]
95
- * @param {import('../../shared/index.js').StorageOptions} [options]
85
+ * @param {import('../../shared/index.js').StorageOptions} options
86
+ * ⚠️ `storage` is REQUIRED. There is no bundled implementation to fall back to, so omitting it
87
+ * throws here rather than quietly writing sessions somewhere this app does not own.
96
88
  * @returns {Promise<import('./types.js').ShopifyHelpers>}
97
89
  */
98
90
  const getShopifyHelpers = async (serverRuntimeConfig, config, { storage: _storage } = {}) => {
99
91
  var _a, _b;
100
92
  const shopify = (0, shopify_js_1.getInitializedShopify)(serverRuntimeConfig);
101
- const { APP_ID, FB_CONFIG, FB_ROOT_COLLECTION, SHOPIFY_RUN_MODE, SESSION_CONFIG, PAYMENT_CONFIG, } = serverRuntimeConfig;
93
+ const { SHOPIFY_RUN_MODE, SESSION_CONFIG, PAYMENT_CONFIG, } = serverRuntimeConfig;
102
94
  const { shop } = config !== null && config !== void 0 ? config : {};
103
95
  const isReviewMode = (0, index_js_1.isTrue)((SHOPIFY_RUN_MODE === 'review') &&
104
96
  (PAYMENT_CONFIG === null || PAYMENT_CONFIG === void 0 ? void 0 : PAYMENT_CONFIG.required));
105
97
  // TODO: should this use our helper instead (and/or as a fallback)??
106
98
  const shopId = (_b = (_a = shopify === null || shopify === void 0 ? void 0 : shopify.utils) === null || _a === void 0 ? void 0 : _a.sanitizeShop) === null || _b === void 0 ? void 0 : _b.call(_a, shop);
107
99
  const cryptoHandler = crypto_js_1.CryptoHandler.getInstance(SESSION_CONFIG);
108
- const storage = (0, index_js_2.ensureStorage)(_storage !== null && _storage !== void 0 ? _storage : await createDefaultStorage(FB_CONFIG, {
109
- collection: FB_ROOT_COLLECTION,
110
- configurationId: APP_ID,
111
- }));
100
+ const storage = (0, index_js_2.ensureStorage)(_storage);
112
101
  // NOTE: this should handle running in next to test non-shopify functionality
113
102
  if (!shopify) {
114
103
  return {
@@ -24,6 +24,11 @@ exports.STORAGE_METHODS = [
24
24
  */
25
25
  const ensureStorage = (storage) => {
26
26
  const candidate = /** @type {Record<string, unknown> | null | undefined} */ (storage);
27
+ // ⚠️ the upgrade path, called out separately: this package used to bundle a firestore store and
28
+ // fall back to it. Listing all seven methods as missing would be true and would not explain why.
29
+ if (!candidate) {
30
+ throw new Error('No session storage supplied. Pass one as `storage` - this package no longer bundles a default.');
31
+ }
27
32
  const missing = (0, display_js_1.ensureArray)(exports.STORAGE_METHODS).filter((method) => (typeof (candidate === null || candidate === void 0 ? void 0 : candidate[method]) !== 'function'));
28
33
  if (missing.length) {
29
34
  throw new Error(`Invalid session storage, missing: ${missing.join(', ')}`);
@@ -30,8 +30,8 @@ export type SessionRecord = {
30
30
  export type StoredState = Record<string, unknown>;
31
31
  /**
32
32
  * What the session layer needs from a store. Two entities, sessions and state, both scoped to a
33
- * shop. Nothing here is Firestore-specific: `@agnostack/next-shopify/storage/firestore` is one
34
- * implementation, and an app can supply its own.
33
+ * shop. This package ships no implementation: an app owns where its sessions live and passes
34
+ * one in.
35
35
  *
36
36
  * ⚠️ Values arrive ALREADY ENCRYPTED and already stripped of anything that must not be persisted.
37
37
  * An implementation stores and returns what it is handed - it must not inspect, reshape or
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agnostack/next-shopify",
3
- "version": "4.0.0-canary.9",
3
+ "version": "4.0.0",
4
4
  "author": "agnoStack Dev <developers@agnostack.com> (https://agnostack.com)",
5
5
  "owner": "agnoStack",
6
6
  "description": "Please contact agnoStack via info@agnostack.com for any questions",
@@ -39,17 +39,6 @@
39
39
  "import": "./dist/esm/lib/index.js",
40
40
  "default": "./dist/esm/lib/index.js"
41
41
  },
42
- "./storage/firestore": {
43
- "types": "./dist/storage/firestore.d.ts",
44
- "require": "./dist/storage/firestore.js",
45
- "import": "./dist/storage/firestore.js",
46
- "default": "./dist/storage/firestore.js"
47
- },
48
- "./storage/firestore/esm": {
49
- "types": "./dist/storage/firestore.d.ts",
50
- "import": "./dist/esm/storage/firestore.js",
51
- "default": "./dist/esm/storage/firestore.js"
52
- },
53
42
  "./runtime": {
54
43
  "types": "./dist/runtime/index.d.ts",
55
44
  "require": "./dist/runtime/index.js",
@@ -123,9 +112,9 @@
123
112
  "uuid": "11.1.1"
124
113
  },
125
114
  "devDependencies": {
126
- "@agnostack/env": "canary",
127
- "@agnostack/next-plugins": "canary",
128
- "@agnostack/verifyd": "canary",
115
+ "@agnostack/env": "latest",
116
+ "@agnostack/next-plugins": "latest",
117
+ "@agnostack/verifyd": "latest",
129
118
  "@commitlint/cli": "21.x",
130
119
  "@commitlint/config-conventional": "21.x",
131
120
  "@eslint/js": "9.x",
@@ -141,7 +130,6 @@
141
130
  "@typescript-eslint/parser": "8.x",
142
131
  "eslint": "9.x",
143
132
  "eslint-plugin-import": "2.x",
144
- "firebase-admin": "14.x",
145
133
  "globals": "16.x",
146
134
  "http-proxy": "1.18.x",
147
135
  "husky": "9.x",
@@ -155,7 +143,6 @@
155
143
  "@agnostack/env": "*",
156
144
  "@agnostack/next-plugins": "*",
157
145
  "@agnostack/verifyd": "*",
158
- "firebase-admin": ">=10.0.0",
159
146
  "http-proxy": ">=1.18.1",
160
147
  "next": ">=15.0.0",
161
148
  "react": ">=18.0.0",
@@ -164,9 +151,6 @@
164
151
  "prompts": ">=2.4.0"
165
152
  },
166
153
  "peerDependenciesMeta": {
167
- "firebase-admin": {
168
- "optional": true
169
- },
170
154
  "next": {
171
155
  "optional": true
172
156
  },
@@ -1,79 +0,0 @@
1
- import { cert, getApp, getApps, initializeApp } from 'firebase-admin/app';
2
- import { getFirestore } from 'firebase-admin/firestore';
3
- import { objectNotEmpty, ensureArray, stringEmpty, STATE_PREFIX, COLLECTION_ROOT, } from '../shared/index.js';
4
- // firebase-admin allows one app per process, so an existing one is reused rather than replaced.
5
- // NOTE: the retired handler also passed `databaseURL`, which configures the Realtime Database.
6
- // Nothing here has ever used it - no `getDatabase`, no `firebase-admin/database` import.
7
- /** @param {any} firebaseConfig */
8
- const initializeFirebaseApp = (firebaseConfig) => (getApps().length
9
- ? getApp()
10
- : initializeApp({ credential: cert(firebaseConfig) }));
11
- /**
12
- * The Firestore implementation of the session-storage contract, kept here so the core never
13
- * imports firebase-admin. Sessions and state share one collection per shop; state is a single
14
- * document per shop, prefixed so it cannot collide with a session id.
15
- *
16
- * @param {any} firebaseConfig a firebase service-account config
17
- * @param {{ collection?: string, configurationId: string }} options
18
- * @returns {import('../shared/types.js').SessionStorage}
19
- */
20
- export const createFirestoreStorage = (firebaseConfig, { collection = COLLECTION_ROOT, configurationId }) => {
21
- if (!objectNotEmpty(firebaseConfig)) {
22
- throw new Error('createFirestoreStorage requires a firebase service-account config');
23
- }
24
- const firestore = getFirestore(initializeFirebaseApp(firebaseConfig));
25
- /** @param {any} shop */
26
- const shopCollection = (shop) => (firestore
27
- .collection(collection)
28
- .doc(shop)
29
- .collection(configurationId));
30
- /** @param {any} shop */
31
- const stateRef = (shop) => shopCollection(shop).doc(`${STATE_PREFIX}${shop}`);
32
- /** @param {any} docRef */
33
- const readDocument = async (docRef) => {
34
- const document = await docRef.get();
35
- return document.exists ? document.data() : undefined;
36
- };
37
- /** @type {import('../shared/types.js').SessionStorage} */
38
- const storage = {
39
- loadSession: async (shopId, sessionId) => (stringEmpty(sessionId)
40
- ? undefined
41
- : readDocument(shopCollection(shopId).doc(`${sessionId}`))),
42
- // NOTE: merge rather than replace. The core writes partial sessions - a billing status
43
- // update carries no access token - and a replacing write would drop the rest.
44
- updateSession: async (shopId, session) => {
45
- const docRef = shopCollection(shopId).doc(session.id);
46
- await docRef.set(session, { merge: true });
47
- return readDocument(docRef);
48
- },
49
- deleteSessions: async (shopId, sessionIds) => {
50
- const collectionRef = shopCollection(shopId);
51
- return Promise.all(ensureArray(sessionIds).map((sessionId) => collectionRef.doc(sessionId).delete()));
52
- },
53
- findSessionIds: async (shopId) => {
54
- // NOTE: empty rather than an error - this is the state on an initial install
55
- if (stringEmpty(shopId)) {
56
- return [];
57
- }
58
- const documents = await shopCollection(shopId).get();
59
- return documents.docs.map((/** @type {any} */ { id }) => id);
60
- },
61
- loadState: async (shop) => (stringEmpty(shop)
62
- ? undefined
63
- : readDocument(stateRef(shop))),
64
- storeState: async (shop, state) => {
65
- if (stringEmpty(shop)) {
66
- return undefined;
67
- }
68
- const docRef = stateRef(shop);
69
- // the contract hands state over as `unknown` on purpose: it belongs to the consumer and is
70
- // stored verbatim, so this cast is the implementation declining to look rather than a guess
71
- await docRef.set(/** @type {any} */ (state));
72
- return readDocument(docRef);
73
- },
74
- deleteState: async (shop) => (stringEmpty(shop)
75
- ? undefined
76
- : stateRef(shop).delete()),
77
- };
78
- return storage;
79
- };
@@ -1,4 +0,0 @@
1
- export function createFirestoreStorage(firebaseConfig: any, { collection, configurationId }: {
2
- collection?: string;
3
- configurationId: string;
4
- }): import("../shared/types.js").SessionStorage;
@@ -1,83 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createFirestoreStorage = void 0;
4
- const app_1 = require("firebase-admin/app");
5
- const firestore_1 = require("firebase-admin/firestore");
6
- const index_js_1 = require("../shared/index.js");
7
- // firebase-admin allows one app per process, so an existing one is reused rather than replaced.
8
- // NOTE: the retired handler also passed `databaseURL`, which configures the Realtime Database.
9
- // Nothing here has ever used it - no `getDatabase`, no `firebase-admin/database` import.
10
- /** @param {any} firebaseConfig */
11
- const initializeFirebaseApp = (firebaseConfig) => ((0, app_1.getApps)().length
12
- ? (0, app_1.getApp)()
13
- : (0, app_1.initializeApp)({ credential: (0, app_1.cert)(firebaseConfig) }));
14
- /**
15
- * The Firestore implementation of the session-storage contract, kept here so the core never
16
- * imports firebase-admin. Sessions and state share one collection per shop; state is a single
17
- * document per shop, prefixed so it cannot collide with a session id.
18
- *
19
- * @param {any} firebaseConfig a firebase service-account config
20
- * @param {{ collection?: string, configurationId: string }} options
21
- * @returns {import('../shared/types.js').SessionStorage}
22
- */
23
- const createFirestoreStorage = (firebaseConfig, { collection = index_js_1.COLLECTION_ROOT, configurationId }) => {
24
- if (!(0, index_js_1.objectNotEmpty)(firebaseConfig)) {
25
- throw new Error('createFirestoreStorage requires a firebase service-account config');
26
- }
27
- const firestore = (0, firestore_1.getFirestore)(initializeFirebaseApp(firebaseConfig));
28
- /** @param {any} shop */
29
- const shopCollection = (shop) => (firestore
30
- .collection(collection)
31
- .doc(shop)
32
- .collection(configurationId));
33
- /** @param {any} shop */
34
- const stateRef = (shop) => shopCollection(shop).doc(`${index_js_1.STATE_PREFIX}${shop}`);
35
- /** @param {any} docRef */
36
- const readDocument = async (docRef) => {
37
- const document = await docRef.get();
38
- return document.exists ? document.data() : undefined;
39
- };
40
- /** @type {import('../shared/types.js').SessionStorage} */
41
- const storage = {
42
- loadSession: async (shopId, sessionId) => ((0, index_js_1.stringEmpty)(sessionId)
43
- ? undefined
44
- : readDocument(shopCollection(shopId).doc(`${sessionId}`))),
45
- // NOTE: merge rather than replace. The core writes partial sessions - a billing status
46
- // update carries no access token - and a replacing write would drop the rest.
47
- updateSession: async (shopId, session) => {
48
- const docRef = shopCollection(shopId).doc(session.id);
49
- await docRef.set(session, { merge: true });
50
- return readDocument(docRef);
51
- },
52
- deleteSessions: async (shopId, sessionIds) => {
53
- const collectionRef = shopCollection(shopId);
54
- return Promise.all((0, index_js_1.ensureArray)(sessionIds).map((sessionId) => collectionRef.doc(sessionId).delete()));
55
- },
56
- findSessionIds: async (shopId) => {
57
- // NOTE: empty rather than an error - this is the state on an initial install
58
- if ((0, index_js_1.stringEmpty)(shopId)) {
59
- return [];
60
- }
61
- const documents = await shopCollection(shopId).get();
62
- return documents.docs.map((/** @type {any} */ { id }) => id);
63
- },
64
- loadState: async (shop) => ((0, index_js_1.stringEmpty)(shop)
65
- ? undefined
66
- : readDocument(stateRef(shop))),
67
- storeState: async (shop, state) => {
68
- if ((0, index_js_1.stringEmpty)(shop)) {
69
- return undefined;
70
- }
71
- const docRef = stateRef(shop);
72
- // the contract hands state over as `unknown` on purpose: it belongs to the consumer and is
73
- // stored verbatim, so this cast is the implementation declining to look rather than a guess
74
- await docRef.set(/** @type {any} */ (state));
75
- return readDocument(docRef);
76
- },
77
- deleteState: async (shop) => ((0, index_js_1.stringEmpty)(shop)
78
- ? undefined
79
- : stateRef(shop).delete()),
80
- };
81
- return storage;
82
- };
83
- exports.createFirestoreStorage = createFirestoreStorage;