@walkeros/server-store-gcs 4.7.0-next-1790722899482 → 4.7.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 +11 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +2 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @walkeros/server-store-gcs
|
|
2
2
|
|
|
3
|
-
## 4.7.0
|
|
3
|
+
## 4.7.0
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
@@ -10,9 +10,16 @@
|
|
|
10
10
|
types of BigQuery, Firehose, PostHog, SQS and Pub/Sub are structural.
|
|
11
11
|
`@walkeros/cli` exports `resolveExportName` and `selectDevExamples` and drops
|
|
12
12
|
`findExample` and `compareOutput`.
|
|
13
|
+
- e860006: `state` gains `mapping` to shape what a lookup returns or a write
|
|
14
|
+
stores, merged into the target. An undeclared `state.store`, or a `file: true`
|
|
15
|
+
store, is now a validation error in `walkeros validate` and deploy preflight;
|
|
16
|
+
at runtime the entry logs an error or is skipped with a warning. The s3 and
|
|
17
|
+
gcs stores report their `file` mode.
|
|
13
18
|
- Updated dependencies [74821ed]
|
|
14
19
|
- Updated dependencies [06b498a]
|
|
15
20
|
- Updated dependencies [74821ed]
|
|
21
|
+
- Updated dependencies [64b06de]
|
|
22
|
+
- Updated dependencies [4b4937f]
|
|
16
23
|
- Updated dependencies [74821ed]
|
|
17
24
|
- Updated dependencies [74821ed]
|
|
18
25
|
- Updated dependencies [74821ed]
|
|
@@ -22,9 +29,11 @@
|
|
|
22
29
|
- Updated dependencies [74821ed]
|
|
23
30
|
- Updated dependencies [74821ed]
|
|
24
31
|
- Updated dependencies [74821ed]
|
|
32
|
+
- Updated dependencies [e860006]
|
|
25
33
|
- Updated dependencies [74821ed]
|
|
26
34
|
- Updated dependencies [74821ed]
|
|
27
|
-
|
|
35
|
+
- Updated dependencies [4f89234]
|
|
36
|
+
- @walkeros/core@4.7.0
|
|
28
37
|
|
|
29
38
|
## 4.6.1
|
|
30
39
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,i={};((e,n)=>{for(var r in n)t(e,r,{get:n[r],enumerable:!0})})(i,{DEFAULT_SETUP:()=>h,default:()=>E,setup:()=>k,storeGcsInit:()=>E}),module.exports=(e=i,((e,i,a,s)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let c of r(i))o.call(e,c)||c===a||t(e,c,{get:()=>i[c],enumerable:!(s=n(i,c))||s.enumerable});return e})(t({},"__esModule",{value:!0}),e));var a=require("@walkeros/core"),s=require("crypto"),c="http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token",u="https://oauth2.googleapis.com/token",l="https://www.googleapis.com/auth/devstorage.read_write",f=6e4;function p(e,t=fetch){let n;return async function(){return n&&Date.now()<n.expiresAt||(n=e?await async function(e,t){const n=Math.floor(Date.now()/1e3),r=function(e,t){const n=g(JSON.stringify({alg:"RS256",typ:"JWT"})),r=g(JSON.stringify(e)),o=(0,s.createSign)("RSA-SHA256");return o.update(`${n}.${r}`),`${n}.${r}.${o.sign(t,"base64url")}`}({iss:e.client_email,scope:l,aud:u,iat:n,exp:n+3600},e.private_key),o=await t(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${r}`});if(!o.ok)throw new Error(`Token exchange error: ${o.status}`);const i=await o.json();return{token:i.access_token,expiresAt:Date.now()+1e3*i.expires_in-f}}(e,t):await async function(e){const t=await e(c,{headers:{"Metadata-Flavor":"Google"},signal:AbortSignal.timeout(5e3)});if(!t.ok)throw new Error(`Metadata server error: ${t.status}`);const n=await t.json();return{token:n.access_token,expiresAt:Date.now()+1e3*n.expires_in-f}}(t)),n.token}}function g(e){return("string"==typeof e?Buffer.from(e):e).toString("base64url")}function d(e){if("string"==typeof e)return e;if(function(e){return"object"==typeof e&&null!==e}(e)&&"string"==typeof e.client_email&&"string"==typeof e.private_key){const{client_email:t,private_key:n,project_id:r}=e;return"string"==typeof r?{client_email:t,private_key:n,project_id:r}:{client_email:t,private_key:n}}}function y(e,t){if(void 0!==e.credentials)return d(e.credentials);const n=e.settings?.credentials;return void 0!==n&&t&&t.warn("gcs store: settings.credentials is deprecated, use config.credentials"),d(n)}var b=require("@walkeros/core");function w(e,t){if(e.projectId)return e.projectId;const n=function(e){if(!e)return;if("string"==typeof e){let t;try{t=JSON.parse(e)}catch{return}return m(t,"project_id")}return m(e,"project_id")}(t);if(n)return n;const r=process.env.GOOGLE_CLOUD_PROJECT;if(r)return r;throw new Error("setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.")}function m(e,t){if(!function(e){return"object"==typeof e&&null!==e}(e))return;const n=e[t];return"string"==typeof n?n:void 0}var v="https://storage.googleapis.com",h={location:"EU",storageClass:"STANDARD",versioning:!1},k=async e=>{const{config:t,logger:n}=e,r=(0,b.resolveSetup)(t.setup,h);if(!r)return void n.debug("setup: skipped (config.setup is false or unset)");const o=t.settings;if(!o||!o.bucket)throw new Error("setup: settings.bucket is required");const i=y(t,n),a=w(r,i),s=function(e){if(!e)return;if("string"==typeof e){const n=JSON.parse(e);return j(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}(i),c=e.env?.fetch??e.config.env?.fetch??fetch,u=p(s,c),l=await u(),f=`${v}/storage/v1/b?project=${encodeURIComponent(a)}`,g=function(e,t){const n={name:e,location:t.location,storageClass:t.storageClass,versioning:{enabled:t.versioning??!1},iamConfiguration:{uniformBucketLevelAccess:{enabled:!0},publicAccessPrevention:"enforced"}};t.labels&&(n.labels=t.labels);t.kmsKeyName&&(n.encryption={defaultKmsKeyName:t.kmsKeyName});t.lifecycle&&(n.lifecycle=t.lifecycle);return n}(o.bucket,r),d=await c(f,{method:"POST",headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json"},body:JSON.stringify(g)});if(200===d.status||201===d.status)return n.info("setup: bucket created",{bucket:o.bucket,projectId:a,location:r.location}),{bucketCreated:!0};if(409!==d.status){const e=await async function(e){try{return await e.text()}catch{return""}}(d);throw new Error(`setup: bucket create failed (${d.status}): ${e}`)}return n.debug("setup: bucket already exists",{bucket:o.bucket}),await async function(e,t,n,r,o){const i=`${v}/storage/v1/b/${encodeURIComponent(e)}`;let a;try{const e=await o(i,{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)return void r.debug("setup: drift check failed (non-fatal)",{status:e.status});a=function(e){if(!j(e))return{};const t={};"string"==typeof e.location&&(t.location=e.location);"string"==typeof e.storageClass&&(t.storageClass=e.storageClass);if(j(e.versioning)){const n=e.versioning.enabled;"boolean"==typeof n&&(t.versioning={enabled:n})}if(j(e.iamConfiguration)){const n={},r=e.iamConfiguration.uniformBucketLevelAccess;j(r)&&"boolean"==typeof r.enabled&&(n.uniformBucketLevelAccess={enabled:r.enabled});const o=e.iamConfiguration.publicAccessPrevention;"string"==typeof o&&(n.publicAccessPrevention=o),t.iamConfiguration=n}if(j(e.labels)){const n={};for(const[t,r]of Object.entries(e.labels))"string"==typeof r&&(n[t]=r);t.labels=n}return t}(await e.json())}catch(e){return void r.debug("setup: drift check failed (non-fatal)",{error:e instanceof Error?e.message:String(e)})}"string"==typeof a.location&&void 0!==n.location&&a.location!==n.location&&r.warn("setup.drift",{field:"location",declared:n.location,actual:a.location});"string"==typeof a.storageClass&&void 0!==n.storageClass&&a.storageClass!==n.storageClass&&r.warn("setup.drift",{field:"storageClass",declared:n.storageClass,actual:a.storageClass});const s=n.versioning??!1,c=a.versioning?.enabled;"boolean"==typeof c&&c!==s&&r.warn("setup.drift",{field:"versioning",declared:s,actual:c});const u=a.iamConfiguration;if(u){const e=u.uniformBucketLevelAccess?.enabled;"boolean"==typeof e&&!0!==e&&r.warn("setup.drift",{field:"uniformBucketLevelAccess",declared:!0,actual:e});const t=u.publicAccessPrevention;"string"==typeof t&&"enforced"!==t&&r.warn("setup.drift",{field:"publicAccessPrevention",declared:"enforced",actual:t})}if(n.labels){const e=a.labels??{};(function(e,t){const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(const r of n)if(e[r]!==t[r])return!1;return!0})(n.labels,e)||r.warn("setup.drift",{field:"labels",declared:n.labels,actual:e})}}(o.bucket,l,r,n,c),{bucketCreated:!1}};function j(e){return"object"==typeof e&&null!==e}var C="https://storage.googleapis.com",$="application/octet-stream",_={js:"application/javascript",mjs:"application/javascript",json:"application/json",css:"text/css",html:"text/html",txt:"text/plain",svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",ico:"image/x-icon",wasm:"application/wasm",map:"application/json"};var A=new Map;function O(e){const t=new ArrayBuffer(e.byteLength),n=new Uint8Array(t);return n.set(e),n}async function S(e,t,n,r,o,i){const a=A.get(e);if(void 0!==a)return void await a;const s=(async()=>{let a;try{const t=await r(),n=`${C}/storage/v1/b/${encodeURIComponent(e)}`;a=await i(n,{method:"HEAD",headers:{Authorization:`Bearer ${t}`}})}catch(t){return o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,error:t instanceof Error?t.message:String(t)}),!0}if(404===a.status){const r=function(e){try{return w({},e)}catch{return"unknown"}}(n);throw new Error(`GCS bucket not found: ${e} in project ${r}. Run "walkeros setup store.${t}" to create it.`)}return a.ok||o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,status:a.status}),!0})();A.set(e,s);try{await s}catch(t){throw A.delete(e),t}}var E=e=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeGcsInit: settings.bucket is required (non-empty string)")}(e.config.settings);const t=e.config.settings,n=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(t.prefix),r=y(e.config,e.logger),o=function(e){if(e){if("string"==typeof e){const n=JSON.parse(e);return function(e){return"object"==typeof e&&null!==e}(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}}(r),i=e.env?.fetch??e.config.env?.fetch??fetch,s=p(o,i),c=t.bucket,u=encodeURIComponent(c),l=e.id,{logger:f}=e,g=!0===e.config.file;function d(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return n+e;f.warn("Invalid key rejected",{key:e})}return{type:"gcs",config:{settings:t,env:e.config.env,id:e.config.id,logger:e.config.logger},setup:k,async get(e){const t=d(e);if(!t)return;let n;await S(c,l,r,s,f,i);try{const e=await s(),r=`${C}/download/storage/v1/b/${u}/o/${encodeURIComponent(t)}?alt=media`,o=await i(r,{headers:{Authorization:`Bearer ${e}`}});if(!o.ok)return;n=Buffer.from(await o.arrayBuffer())}catch{return}if(g)return n;try{return(0,a.deserializeStoreValue)(n)}catch(t){return void f.debug("structured decode failed, degrading to miss",{key:e,error:t instanceof Error?t.message:String(t)})}},async set(e,t){const n=d(e);if(!n)return;let o,p;if(g){if("string"==typeof t)o=O(Buffer.from(t));else{if(!(t instanceof Uint8Array))throw new Error("storeGcsInit.set: in file mode value must be Uint8Array or string; got "+typeof t);o=O(t)}p=function(e){const t=e.lastIndexOf(".");if(t<0||t===e.length-1)return $;const n=e.slice(t+1).toLowerCase();return _[n]??$}(e)}else o=O((0,a.serializeStoreValue)(t)),p="application/json";await S(c,l,r,s,f,i);const y=await s(),b=`${C}/upload/storage/v1/b/${u}/o?uploadType=media&name=${encodeURIComponent(n)}`;await i(b,{method:"POST",headers:{Authorization:`Bearer ${y}`,"Content-Type":p},body:o})},async delete(e){const t=d(e);if(t){await S(c,l,r,s,f,i);try{const e=await s(),n=`${C}/storage/v1/b/${u}/o/${encodeURIComponent(t)}`;await i(n,{method:"DELETE",headers:{Authorization:`Bearer ${e}`}})}catch{}}}}};//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var e,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,i={};((e,n)=>{for(var r in n)t(e,r,{get:n[r],enumerable:!0})})(i,{DEFAULT_SETUP:()=>h,default:()=>E,setup:()=>k,storeGcsInit:()=>E}),module.exports=(e=i,((e,i,a,s)=>{if(i&&"object"==typeof i||"function"==typeof i)for(let c of r(i))o.call(e,c)||c===a||t(e,c,{get:()=>i[c],enumerable:!(s=n(i,c))||s.enumerable});return e})(t({},"__esModule",{value:!0}),e));var a=require("@walkeros/core"),s=require("crypto"),c="http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token",u="https://oauth2.googleapis.com/token",l="https://www.googleapis.com/auth/devstorage.read_write",f=6e4;function p(e,t=fetch){let n;return async function(){return n&&Date.now()<n.expiresAt||(n=e?await async function(e,t){const n=Math.floor(Date.now()/1e3),r=function(e,t){const n=g(JSON.stringify({alg:"RS256",typ:"JWT"})),r=g(JSON.stringify(e)),o=(0,s.createSign)("RSA-SHA256");return o.update(`${n}.${r}`),`${n}.${r}.${o.sign(t,"base64url")}`}({iss:e.client_email,scope:l,aud:u,iat:n,exp:n+3600},e.private_key),o=await t(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${r}`});if(!o.ok)throw new Error(`Token exchange error: ${o.status}`);const i=await o.json();return{token:i.access_token,expiresAt:Date.now()+1e3*i.expires_in-f}}(e,t):await async function(e){const t=await e(c,{headers:{"Metadata-Flavor":"Google"},signal:AbortSignal.timeout(5e3)});if(!t.ok)throw new Error(`Metadata server error: ${t.status}`);const n=await t.json();return{token:n.access_token,expiresAt:Date.now()+1e3*n.expires_in-f}}(t)),n.token}}function g(e){return("string"==typeof e?Buffer.from(e):e).toString("base64url")}function d(e){if("string"==typeof e)return e;if(function(e){return"object"==typeof e&&null!==e}(e)&&"string"==typeof e.client_email&&"string"==typeof e.private_key){const{client_email:t,private_key:n,project_id:r}=e;return"string"==typeof r?{client_email:t,private_key:n,project_id:r}:{client_email:t,private_key:n}}}function y(e,t){if(void 0!==e.credentials)return d(e.credentials);const n=e.settings?.credentials;return void 0!==n&&t&&t.warn("gcs store: settings.credentials is deprecated, use config.credentials"),d(n)}var b=require("@walkeros/core");function w(e,t){if(e.projectId)return e.projectId;const n=function(e){if(!e)return;if("string"==typeof e){let t;try{t=JSON.parse(e)}catch{return}return m(t,"project_id")}return m(e,"project_id")}(t);if(n)return n;const r=process.env.GOOGLE_CLOUD_PROJECT;if(r)return r;throw new Error("setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.")}function m(e,t){if(!function(e){return"object"==typeof e&&null!==e}(e))return;const n=e[t];return"string"==typeof n?n:void 0}var v="https://storage.googleapis.com",h={location:"EU",storageClass:"STANDARD",versioning:!1},k=async e=>{const{config:t,logger:n}=e,r=(0,b.resolveSetup)(t.setup,h);if(!r)return void n.debug("setup: skipped (config.setup is false or unset)");const o=t.settings;if(!o||!o.bucket)throw new Error("setup: settings.bucket is required");const i=y(t,n),a=w(r,i),s=function(e){if(!e)return;if("string"==typeof e){const n=JSON.parse(e);return j(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}(i),c=e.env?.fetch??e.config.env?.fetch??fetch,u=p(s,c),l=await u(),f=`${v}/storage/v1/b?project=${encodeURIComponent(a)}`,g=function(e,t){const n={name:e,location:t.location,storageClass:t.storageClass,versioning:{enabled:t.versioning??!1},iamConfiguration:{uniformBucketLevelAccess:{enabled:!0},publicAccessPrevention:"enforced"}};t.labels&&(n.labels=t.labels);t.kmsKeyName&&(n.encryption={defaultKmsKeyName:t.kmsKeyName});t.lifecycle&&(n.lifecycle=t.lifecycle);return n}(o.bucket,r),d=await c(f,{method:"POST",headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json"},body:JSON.stringify(g)});if(200===d.status||201===d.status)return n.info("setup: bucket created",{bucket:o.bucket,projectId:a,location:r.location}),{bucketCreated:!0};if(409!==d.status){const e=await async function(e){try{return await e.text()}catch{return""}}(d);throw new Error(`setup: bucket create failed (${d.status}): ${e}`)}return n.debug("setup: bucket already exists",{bucket:o.bucket}),await async function(e,t,n,r,o){const i=`${v}/storage/v1/b/${encodeURIComponent(e)}`;let a;try{const e=await o(i,{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)return void r.debug("setup: drift check failed (non-fatal)",{status:e.status});a=function(e){if(!j(e))return{};const t={};"string"==typeof e.location&&(t.location=e.location);"string"==typeof e.storageClass&&(t.storageClass=e.storageClass);if(j(e.versioning)){const n=e.versioning.enabled;"boolean"==typeof n&&(t.versioning={enabled:n})}if(j(e.iamConfiguration)){const n={},r=e.iamConfiguration.uniformBucketLevelAccess;j(r)&&"boolean"==typeof r.enabled&&(n.uniformBucketLevelAccess={enabled:r.enabled});const o=e.iamConfiguration.publicAccessPrevention;"string"==typeof o&&(n.publicAccessPrevention=o),t.iamConfiguration=n}if(j(e.labels)){const n={};for(const[t,r]of Object.entries(e.labels))"string"==typeof r&&(n[t]=r);t.labels=n}return t}(await e.json())}catch(e){return void r.debug("setup: drift check failed (non-fatal)",{error:e instanceof Error?e.message:String(e)})}"string"==typeof a.location&&void 0!==n.location&&a.location!==n.location&&r.warn("setup.drift",{field:"location",declared:n.location,actual:a.location});"string"==typeof a.storageClass&&void 0!==n.storageClass&&a.storageClass!==n.storageClass&&r.warn("setup.drift",{field:"storageClass",declared:n.storageClass,actual:a.storageClass});const s=n.versioning??!1,c=a.versioning?.enabled;"boolean"==typeof c&&c!==s&&r.warn("setup.drift",{field:"versioning",declared:s,actual:c});const u=a.iamConfiguration;if(u){const e=u.uniformBucketLevelAccess?.enabled;"boolean"==typeof e&&!0!==e&&r.warn("setup.drift",{field:"uniformBucketLevelAccess",declared:!0,actual:e});const t=u.publicAccessPrevention;"string"==typeof t&&"enforced"!==t&&r.warn("setup.drift",{field:"publicAccessPrevention",declared:"enforced",actual:t})}if(n.labels){const e=a.labels??{};(function(e,t){const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(const r of n)if(e[r]!==t[r])return!1;return!0})(n.labels,e)||r.warn("setup.drift",{field:"labels",declared:n.labels,actual:e})}}(o.bucket,l,r,n,c),{bucketCreated:!1}};function j(e){return"object"==typeof e&&null!==e}var C="https://storage.googleapis.com",$="application/octet-stream",_={js:"application/javascript",mjs:"application/javascript",json:"application/json",css:"text/css",html:"text/html",txt:"text/plain",svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",ico:"image/x-icon",wasm:"application/wasm",map:"application/json"};var A=new Map;function O(e){const t=new ArrayBuffer(e.byteLength),n=new Uint8Array(t);return n.set(e),n}async function S(e,t,n,r,o,i){const a=A.get(e);if(void 0!==a)return void await a;const s=(async()=>{let a;try{const t=await r(),n=`${C}/storage/v1/b/${encodeURIComponent(e)}`;a=await i(n,{method:"HEAD",headers:{Authorization:`Bearer ${t}`}})}catch(t){return o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,error:t instanceof Error?t.message:String(t)}),!0}if(404===a.status){const r=function(e){try{return w({},e)}catch{return"unknown"}}(n);throw new Error(`GCS bucket not found: ${e} in project ${r}. Run "walkeros setup store.${t}" to create it.`)}return a.ok||o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,status:a.status}),!0})();A.set(e,s);try{await s}catch(t){throw A.delete(e),t}}var E=e=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeGcsInit: settings.bucket is required (non-empty string)")}(e.config.settings);const t=e.config.settings,n=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(t.prefix),r=y(e.config,e.logger),o=function(e){if(e){if("string"==typeof e){const n=JSON.parse(e);return function(e){return"object"==typeof e&&null!==e}(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}}(r),i=e.env?.fetch??e.config.env?.fetch??fetch,s=p(o,i),c=t.bucket,u=encodeURIComponent(c),l=e.id,{logger:f}=e,g=!0===e.config.file;function d(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return n+e;f.warn("Invalid key rejected",{key:e})}return{type:"gcs",config:{settings:t,env:e.config.env,id:e.config.id,logger:e.config.logger,file:e.config.file},setup:k,async get(e){const t=d(e);if(!t)return;let n;await S(c,l,r,s,f,i);try{const e=await s(),r=`${C}/download/storage/v1/b/${u}/o/${encodeURIComponent(t)}?alt=media`,o=await i(r,{headers:{Authorization:`Bearer ${e}`}});if(!o.ok)return;n=Buffer.from(await o.arrayBuffer())}catch{return}if(g)return n;try{return(0,a.deserializeStoreValue)(n)}catch(t){return void f.debug("structured decode failed, degrading to miss",{key:e,error:t instanceof Error?t.message:String(t)})}},async set(e,t){const n=d(e);if(!n)return;let o,p;if(g){if("string"==typeof t)o=O(Buffer.from(t));else{if(!(t instanceof Uint8Array))throw new Error("storeGcsInit.set: in file mode value must be Uint8Array or string; got "+typeof t);o=O(t)}p=function(e){const t=e.lastIndexOf(".");if(t<0||t===e.length-1)return $;const n=e.slice(t+1).toLowerCase();return _[n]??$}(e)}else o=O((0,a.serializeStoreValue)(t)),p="application/json";await S(c,l,r,s,f,i);const y=await s(),b=`${C}/upload/storage/v1/b/${u}/o?uploadType=media&name=${encodeURIComponent(n)}`;await i(b,{method:"POST",headers:{Authorization:`Bearer ${y}`,"Content-Type":p},body:o})},async delete(e){const t=d(e);if(t){await S(c,l,r,s,f,i);try{const e=await s(),n=`${C}/storage/v1/b/${u}/o/${encodeURIComponent(t)}`;await i(n,{method:"DELETE",headers:{Authorization:`Bearer ${e}`}})}catch{}}}}};//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/auth.ts","../src/credentials.ts","../src/setup.ts","../src/setup-helpers.ts"],"sourcesContent":["export { storeGcsInit } from './store';\nexport { setup, DEFAULT_SETUP } from './setup';\nexport type {\n Env,\n GcsStoreSettings,\n ServiceAccountCredentials,\n Setup,\n Types,\n} from './types';\nexport { storeGcsInit as default } from './store';\n","import type { Logger, Store } from '@walkeros/core';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type {\n GcsStoreSettings,\n ServiceAccountCredentials,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { setup as gcsSetup } from './setup';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\nconst JSON_CONTENT_TYPE = 'application/json';\nconst DEFAULT_CONTENT_TYPE = 'application/octet-stream';\n\n// Minimal extension to content-type map for the file-mode asset-serving use\n// case (serving walker.js and friends back with a correct mime). Anything not\n// listed falls back to application/octet-stream.\nconst MIME_BY_EXT: Readonly<Record<string, string>> = {\n js: 'application/javascript',\n mjs: 'application/javascript',\n json: 'application/json',\n css: 'text/css',\n html: 'text/html',\n txt: 'text/plain',\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n ico: 'image/x-icon',\n wasm: 'application/wasm',\n map: 'application/json',\n};\n\nfunction mimeFromKey(key: string): string {\n const dot = key.lastIndexOf('.');\n if (dot < 0 || dot === key.length - 1) return DEFAULT_CONTENT_TYPE;\n const ext = key.slice(dot + 1).toLowerCase();\n return MIME_BY_EXT[ext] ?? DEFAULT_CONTENT_TYPE;\n}\n\n/**\n * Module-level cache for the bucket existence pre-check. Keyed by bucket\n * name (bucket names are globally unique in GCS). One HEAD per process per\n * bucket. The cache stores a Promise so concurrent first-operations share a\n * single in-flight request.\n *\n * Exported helpers below allow tests to reset/seed the cache.\n */\nconst bucketExistsCache: Map<string, Promise<boolean>> = new Map();\n\n/** @internal Test-only: clear the existence cache. */\nexport function __resetBucketExistenceCache(): void {\n bucketExistsCache.clear();\n}\n\n/** @internal Test-only: seed the existence cache for a bucket. */\nexport function __seedBucketExists(bucket: string): void {\n bucketExistsCache.set(bucket, Promise.resolve(true));\n}\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\n\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\n/**\n * Wrap bytes in a fresh Uint8Array backed by a plain ArrayBuffer so they\n * satisfy fetch's BodyInit contract. Avoids type casts: Node's Buffer\n * type is `Buffer<ArrayBufferLike>`, while the DOM's BodyInit (used by\n * fetch's RequestInit) expects `ArrayBufferView<ArrayBuffer>`. Accepts any\n * Uint8Array (Buffer included, plus the codec's plain Uint8Array output).\n */\nfunction bufferToBody(buf: Uint8Array): Uint8Array<ArrayBuffer> {\n const out = new ArrayBuffer(buf.byteLength);\n const view = new Uint8Array(out);\n view.set(buf);\n return view;\n}\n\nfunction assertGcsSettings(\n settings: Partial<GcsStoreSettings> | undefined,\n): asserts settings is GcsStoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeGcsInit: settings.bucket is required (non-empty string)',\n );\n }\n}\n\n/**\n * Resolve a project ID for the runtime hard-fail message. Uses the same\n * resolution order as setup but never throws: returns \"unknown\" when no\n * source is available so the error message is still actionable.\n */\nfunction resolveProjectIdForMessage(\n credentials: Store.Credentials<Types> | undefined,\n): string {\n try {\n return resolveProjectId({}, credentials);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Verify the bucket exists. Issues `HEAD /storage/v1/b/<bucket>` once per\n * process per bucket. On 404, throws an actionable error pointing at the\n * setup command. On any other failure (non-2xx response or transport\n * error), treats the result as \"exists\" to avoid blocking legitimate\n * operations on a transient blip.\n */\nasync function ensureBucketExists(\n bucket: string,\n id: string,\n credentials: Store.Credentials<Types> | undefined,\n getToken: () => Promise<string>,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const existing = bucketExistsCache.get(bucket);\n if (existing !== undefined) {\n await existing;\n return;\n }\n\n const promise = (async (): Promise<boolean> => {\n let res: Response;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n res = await doFetch(url, {\n method: 'HEAD',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch (err) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n error: err instanceof Error ? err.message : String(err),\n });\n return true;\n }\n\n if (res.status === 404) {\n const projectId = resolveProjectIdForMessage(credentials);\n throw new Error(\n `GCS bucket not found: ${bucket} in project ${projectId}. Run \"walkeros setup store.${id}\" to create it.`,\n );\n }\n if (!res.ok) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n status: res.status,\n });\n }\n return true;\n })();\n\n bucketExistsCache.set(bucket, promise);\n try {\n await promise;\n } catch (err) {\n // Drop the failing entry so the next call retries the check.\n bucketExistsCache.delete(bucket);\n throw err;\n }\n}\n\nexport const storeGcsInit: Store.Init<Types> = (context) => {\n assertGcsSettings(context.config.settings);\n const settings: GcsStoreSettings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\n const rawCreds = resolveCredentials(context.config, context.logger);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included; the global fetch is the runtime default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const bucketRaw = settings.bucket;\n const bucket = encodeURIComponent(bucketRaw);\n const id = context.id;\n const { logger } = context;\n // One mode per instance, decided at init. file:true persists raw bytes\n // byte-exact with a real mime; structured mode round-trips StoreValue\n // through the shared core codec, stored as application/json.\n const fileMode = context.config.file === true;\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n const config: Store.Config<Types> = {\n settings,\n env: context.config.env,\n id: context.config.id,\n logger: context.config.logger,\n // `setup` from the incoming context.config is typed as `unknown` because\n // the Init context narrows only the settings/env/initSettings slots and\n // not the setup-options slot. The flow runtime never uses this field at\n // runtime (setup is invoked separately by the CLI), so omit it from the\n // returned config without a cast.\n };\n\n return {\n type: 'gcs',\n config,\n setup: gcsSetup,\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return undefined;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n let bytes: Buffer;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/download/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}?alt=media`;\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return undefined;\n bytes = Buffer.from(await res.arrayBuffer());\n } catch {\n return undefined;\n }\n // File mode hands the raw bytes back untouched (Buffer is a Uint8Array,\n // a valid StoreValue leaf). Structured mode decodes the utf8-JSON payload.\n // A corrupt or empty payload (GCS can return ok:true with an empty body)\n // degrades to a miss rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n logger.debug('structured decode failed, degrading to miss', {\n key,\n error: err instanceof Error ? err.message : String(err),\n });\n return undefined;\n }\n },\n\n async set(key: string, value: Store.StoreValue): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n let body: Uint8Array<ArrayBuffer>;\n let contentType: string;\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (typeof value === 'string') {\n body = bufferToBody(Buffer.from(value));\n } else if (value instanceof Uint8Array) {\n body = bufferToBody(value);\n } else {\n throw new Error(\n 'storeGcsInit.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n contentType = mimeFromKey(key);\n } else {\n // Structured mode: serialize to utf8-JSON bytes, uploaded as JSON.\n body = bufferToBody(serializeStoreValue(value));\n contentType = JSON_CONTENT_TYPE;\n }\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n const token = await getToken();\n const url = `${GCS_BASE}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': contentType,\n },\n body,\n });\n },\n\n async delete(key: string): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch {\n /* GCS delete is idempotent */\n }\n },\n };\n};\n","import { createSign } from 'node:crypto';\nimport type { ServiceAccountCredentials } from './types';\n\nexport type TokenProvider = () => Promise<string>;\n\nconst METADATA_URL =\n 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token';\nconst OAUTH_URL = 'https://oauth2.googleapis.com/token';\nconst SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write';\nconst REFRESH_MARGIN_MS = 60_000;\n\ninterface CachedToken {\n token: string;\n expiresAt: number;\n}\n\nexport function createTokenProvider(\n credentials?: ServiceAccountCredentials,\n doFetch: typeof fetch = fetch,\n): TokenProvider {\n let cached: CachedToken | undefined;\n\n return async function getToken(): Promise<string> {\n if (cached && Date.now() < cached.expiresAt) return cached.token;\n\n cached = credentials\n ? await fetchServiceAccountToken(credentials, doFetch)\n : await fetchMetadataToken(doFetch);\n\n return cached.token;\n };\n}\n\nasync function fetchMetadataToken(doFetch: typeof fetch): Promise<CachedToken> {\n const res = await doFetch(METADATA_URL, {\n headers: { 'Metadata-Flavor': 'Google' },\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) throw new Error(`Metadata server error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nasync function fetchServiceAccountToken(\n creds: ServiceAccountCredentials,\n doFetch: typeof fetch,\n): Promise<CachedToken> {\n const now = Math.floor(Date.now() / 1000);\n const jwt = signJwt(\n {\n iss: creds.client_email,\n scope: SCOPE,\n aud: OAUTH_URL,\n iat: now,\n exp: now + 3600,\n },\n creds.private_key,\n );\n\n const res = await doFetch(OAUTH_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,\n });\n if (!res.ok) throw new Error(`Token exchange error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nfunction base64url(input: string | Buffer): string {\n const buf = typeof input === 'string' ? Buffer.from(input) : input;\n return buf.toString('base64url');\n}\n\nfunction signJwt(payload: object, privateKey: string): string {\n const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' }));\n const body = base64url(JSON.stringify(payload));\n const sign = createSign('RSA-SHA256');\n sign.update(`${header}.${body}`);\n return `${header}.${body}.${sign.sign(privateKey, 'base64url')}`;\n}\n","import type { Logger, Store } from '@walkeros/core';\nimport type { Types } from './types';\n\ntype ResolvedCredentials = Store.Credentials<Types>;\n\n/**\n * The two credential read sites narrow different slots: `store.ts` runs inside\n * the `Store.Init` context, where the typed `credentials` slot is widened back\n * to `unknown` (settings is `Partial`); `setup.ts` runs with the full\n * `Store.Config<Types>`, where `credentials` is `ResolvedCredentials`. Accept\n * `unknown` for both and narrow at runtime.\n */\ninterface CredentialSources {\n credentials?: unknown;\n settings?: { credentials?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow an unknown credential candidate to the resolved credential shape: a\n * JSON string (often a `$env` reference) or a parsed service-account object.\n * Anything else resolves to `undefined`; the downstream parser surfaces a\n * misconfiguration rather than this layer guessing.\n */\nfunction asCredential(value: unknown): ResolvedCredentials | undefined {\n if (typeof value === 'string') return value;\n if (\n isRecord(value) &&\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n ) {\n const { client_email, private_key, project_id } = value;\n return typeof project_id === 'string'\n ? { client_email, private_key, project_id }\n : { client_email, private_key };\n }\n return undefined;\n}\n\n/**\n * Resolve the credential source for the store, preferring the strictly-typed\n * `config.credentials` slot and falling back to the deprecated\n * `settings.credentials`. Returns the raw credential (string or object); the\n * caller parses it into a service account.\n *\n * Precedence is `??` (not `||`) so an explicitly-empty `config.credentials`\n * surfaces as a misconfiguration rather than silently falling through.\n *\n * Warn-once semantics: the warning fires from the init/setup read site, which\n * runs once per store instance. No module-level flag, so a second instance on\n * the deprecated path still warns.\n */\nexport function resolveCredentials(\n config: CredentialSources,\n logger?: Logger.Instance,\n): ResolvedCredentials | undefined {\n if (config.credentials !== undefined) return asCredential(config.credentials);\n\n const fromSettings = config.settings?.credentials;\n if (fromSettings !== undefined && logger) {\n logger.warn(\n 'gcs store: settings.credentials is deprecated, use config.credentials',\n );\n }\n return asCredential(fromSettings);\n}\n","import type { LifecycleContext, Logger, Store, SetupFn } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type {\n Env,\n GcsStoreSettings,\n ServiceAccountCredentials,\n Setup,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\n/**\n * Default setup options. Optional fields (lifecycle, kmsKeyName, labels,\n * projectId) are intentionally omitted so the resolved options carry\n * `undefined` for them and the body builder can skip writing those\n * properties to the create request payload.\n */\nexport const DEFAULT_SETUP: Setup = {\n location: 'EU',\n storageClass: 'STANDARD',\n versioning: false,\n};\n\nexport interface SetupResult {\n bucketCreated: boolean;\n}\n\n/**\n * Public alias kept for callers that imported the prior shape.\n * Equivalent to the framework's `Store.Config<Types>`.\n */\nexport type GcsStoreConfig = Store.Config<Types>;\n\n/**\n * Provision a GCS bucket described in the flow config.\n * Idempotent: 409 means the bucket exists, drift is logged but never patched.\n * Never auto-mutates an existing bucket.\n */\nexport const setup: SetupFn<GcsStoreConfig, Env> = async (\n context: LifecycleContext<GcsStoreConfig, Env>,\n) => {\n const { config, logger } = context;\n const options = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!options) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n\n const settings = config.settings;\n if (!settings || !settings.bucket) {\n throw new Error('setup: settings.bucket is required');\n }\n\n const rawCreds = resolveCredentials(config, logger);\n const projectId = resolveProjectId(options, rawCreds);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included, as in the store runtime; the global fetch is the default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const token = await getToken();\n\n const createUrl = `${GCS_BASE}/storage/v1/b?project=${encodeURIComponent(projectId)}`;\n const body = buildCreateBody(settings.bucket, options);\n\n const createRes = await doFetch(createUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (createRes.status === 200 || createRes.status === 201) {\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n projectId,\n location: options.location,\n });\n return { bucketCreated: true };\n }\n\n if (createRes.status !== 409) {\n const text = await safeText(createRes);\n throw new Error(\n `setup: bucket create failed (${createRes.status}): ${text}`,\n );\n }\n\n logger.debug('setup: bucket already exists', { bucket: settings.bucket });\n await detectDrift(settings.bucket, token, options, logger, doFetch);\n return { bucketCreated: false };\n};\n\ninterface CreateBody {\n name: string;\n location?: string;\n storageClass?: string;\n versioning: { enabled: boolean };\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true };\n publicAccessPrevention: 'enforced';\n };\n labels?: Record<string, string>;\n encryption?: { defaultKmsKeyName: string };\n lifecycle?: { rule: unknown[] };\n}\n\nfunction buildCreateBody(bucket: string, options: Setup): CreateBody {\n const body: CreateBody = {\n name: bucket,\n location: options.location,\n storageClass: options.storageClass,\n versioning: { enabled: options.versioning ?? false },\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true },\n publicAccessPrevention: 'enforced',\n },\n };\n if (options.labels) body.labels = options.labels;\n if (options.kmsKeyName) {\n body.encryption = { defaultKmsKeyName: options.kmsKeyName };\n }\n if (options.lifecycle) body.lifecycle = options.lifecycle;\n return body;\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '';\n }\n}\n\ninterface BucketMetadata {\n location?: string;\n storageClass?: string;\n versioning?: { enabled?: boolean };\n iamConfiguration?: {\n uniformBucketLevelAccess?: { enabled?: boolean };\n publicAccessPrevention?: string;\n };\n labels?: Record<string, string>;\n}\n\nasync function detectDrift(\n bucket: string,\n token: string,\n options: Setup,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n let metadata: BucketMetadata;\n try {\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) {\n logger.debug('setup: drift check failed (non-fatal)', {\n status: res.status,\n });\n return;\n }\n const parsed: unknown = await res.json();\n metadata = readBucketMetadata(parsed);\n } catch (err) {\n logger.debug('setup: drift check failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n if (\n typeof metadata.location === 'string' &&\n options.location !== undefined &&\n metadata.location !== options.location\n ) {\n logger.warn('setup.drift', {\n field: 'location',\n declared: options.location,\n actual: metadata.location,\n });\n }\n\n if (\n typeof metadata.storageClass === 'string' &&\n options.storageClass !== undefined &&\n metadata.storageClass !== options.storageClass\n ) {\n logger.warn('setup.drift', {\n field: 'storageClass',\n declared: options.storageClass,\n actual: metadata.storageClass,\n });\n }\n\n const declaredVersioning = options.versioning ?? false;\n const actualVersioning = metadata.versioning?.enabled;\n if (\n typeof actualVersioning === 'boolean' &&\n actualVersioning !== declaredVersioning\n ) {\n logger.warn('setup.drift', {\n field: 'versioning',\n declared: declaredVersioning,\n actual: actualVersioning,\n });\n }\n\n const iam = metadata.iamConfiguration;\n if (iam) {\n const actualUniform = iam.uniformBucketLevelAccess?.enabled;\n if (typeof actualUniform === 'boolean' && actualUniform !== true) {\n logger.warn('setup.drift', {\n field: 'uniformBucketLevelAccess',\n declared: true,\n actual: actualUniform,\n });\n }\n const actualPap = iam.publicAccessPrevention;\n if (typeof actualPap === 'string' && actualPap !== 'enforced') {\n logger.warn('setup.drift', {\n field: 'publicAccessPrevention',\n declared: 'enforced',\n actual: actualPap,\n });\n }\n }\n\n if (options.labels) {\n const actualLabels = metadata.labels ?? {};\n if (!labelsEqual(options.labels, actualLabels)) {\n logger.warn('setup.drift', {\n field: 'labels',\n declared: options.labels,\n actual: actualLabels,\n });\n }\n }\n}\n\nfunction labelsEqual(\n a: Record<string, string>,\n b: Record<string, string>,\n): boolean {\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (a[key] !== b[key]) return false;\n }\n return true;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readBucketMetadata(value: unknown): BucketMetadata {\n if (!isRecord(value)) return {};\n const out: BucketMetadata = {};\n if (typeof value.location === 'string') out.location = value.location;\n if (typeof value.storageClass === 'string') {\n out.storageClass = value.storageClass;\n }\n if (isRecord(value.versioning)) {\n const enabled = value.versioning.enabled;\n if (typeof enabled === 'boolean') {\n out.versioning = { enabled };\n }\n }\n if (isRecord(value.iamConfiguration)) {\n const iam: BucketMetadata['iamConfiguration'] = {};\n const uniform = value.iamConfiguration.uniformBucketLevelAccess;\n if (isRecord(uniform) && typeof uniform.enabled === 'boolean') {\n iam.uniformBucketLevelAccess = { enabled: uniform.enabled };\n }\n const pap = value.iamConfiguration.publicAccessPrevention;\n if (typeof pap === 'string') iam.publicAccessPrevention = pap;\n out.iamConfiguration = iam;\n }\n if (isRecord(value.labels)) {\n const labels: Record<string, string> = {};\n for (const [k, v] of Object.entries(value.labels)) {\n if (typeof v === 'string') labels[k] = v;\n }\n out.labels = labels;\n }\n return out;\n}\n","import type { Store } from '@walkeros/core';\nimport type { ServiceAccountCredentials, Types } from './types';\n\n/**\n * Resolves the GCP project ID used to create a bucket in the GCS JSON API.\n *\n * Resolution order:\n * 1. `setup.projectId` (explicit override).\n * 2. the resolved credentials' `project_id` (when SA JSON is parsed and contains it).\n * 3. `process.env.GOOGLE_CLOUD_PROJECT` (Cloud Run / GKE convention).\n * 4. Throw with an actionable error message.\n *\n * `process.env.GOOGLE_CLOUD_PROJECT` is read at call time, not at module load,\n * so changes after import are honored.\n *\n * `credentials` is the already-resolved value (`config.credentials ??\n * settings.credentials`), not read off settings here, so `config.credentials`\n * is honored for project-id derivation.\n */\nexport function resolveProjectId(\n setup: { projectId?: string },\n credentials: Store.Credentials<Types> | undefined,\n): string {\n if (setup.projectId) return setup.projectId;\n\n const fromCreds = extractProjectIdFromCredentials(credentials);\n if (fromCreds) return fromCreds;\n\n const fromEnv = process.env.GOOGLE_CLOUD_PROJECT;\n if (fromEnv) return fromEnv;\n\n throw new Error(\n 'setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.',\n );\n}\n\n/**\n * Typed predicate: returns the `project_id` field from credentials when\n * present and a string. Avoids type casts by validating the runtime shape.\n *\n * - `string` credentials are parsed as JSON; non-JSON or non-object payloads\n * return undefined.\n * - object credentials are inspected directly. Real SA JSON contains\n * `project_id` even though `ServiceAccountCredentials` declares only the\n * fields the runtime needs (client_email, private_key).\n */\nfunction extractProjectIdFromCredentials(\n credentials: Store.Credentials<Types> | undefined,\n): string | undefined {\n if (!credentials) return undefined;\n\n if (typeof credentials === 'string') {\n let parsed: unknown;\n try {\n parsed = JSON.parse(credentials);\n } catch {\n return undefined;\n }\n return readStringField(parsed, 'project_id');\n }\n\n return readStringField(credentials, 'project_id');\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readStringField(\n value: ServiceAccountCredentials | unknown,\n field: string,\n): string | undefined {\n if (!isRecord(value)) return undefined;\n const fieldValue = value[field];\n return typeof fieldValue === 'string' ? fieldValue : undefined;\n}\n"],"mappings":"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","names":["import_core","setup","isRecord","isRecord","GCS_BASE","isRecord","isServiceAccountShape","parseCredentials","GCS_BASE"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/auth.ts","../src/credentials.ts","../src/setup.ts","../src/setup-helpers.ts"],"sourcesContent":["export { storeGcsInit } from './store';\nexport { setup, DEFAULT_SETUP } from './setup';\nexport type {\n Env,\n GcsStoreSettings,\n ServiceAccountCredentials,\n Setup,\n Types,\n} from './types';\nexport { storeGcsInit as default } from './store';\n","import type { Logger, Store } from '@walkeros/core';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type {\n GcsStoreSettings,\n ServiceAccountCredentials,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { setup as gcsSetup } from './setup';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\nconst JSON_CONTENT_TYPE = 'application/json';\nconst DEFAULT_CONTENT_TYPE = 'application/octet-stream';\n\n// Minimal extension to content-type map for the file-mode asset-serving use\n// case (serving walker.js and friends back with a correct mime). Anything not\n// listed falls back to application/octet-stream.\nconst MIME_BY_EXT: Readonly<Record<string, string>> = {\n js: 'application/javascript',\n mjs: 'application/javascript',\n json: 'application/json',\n css: 'text/css',\n html: 'text/html',\n txt: 'text/plain',\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n ico: 'image/x-icon',\n wasm: 'application/wasm',\n map: 'application/json',\n};\n\nfunction mimeFromKey(key: string): string {\n const dot = key.lastIndexOf('.');\n if (dot < 0 || dot === key.length - 1) return DEFAULT_CONTENT_TYPE;\n const ext = key.slice(dot + 1).toLowerCase();\n return MIME_BY_EXT[ext] ?? DEFAULT_CONTENT_TYPE;\n}\n\n/**\n * Module-level cache for the bucket existence pre-check. Keyed by bucket\n * name (bucket names are globally unique in GCS). One HEAD per process per\n * bucket. The cache stores a Promise so concurrent first-operations share a\n * single in-flight request.\n *\n * Exported helpers below allow tests to reset/seed the cache.\n */\nconst bucketExistsCache: Map<string, Promise<boolean>> = new Map();\n\n/** @internal Test-only: clear the existence cache. */\nexport function __resetBucketExistenceCache(): void {\n bucketExistsCache.clear();\n}\n\n/** @internal Test-only: seed the existence cache for a bucket. */\nexport function __seedBucketExists(bucket: string): void {\n bucketExistsCache.set(bucket, Promise.resolve(true));\n}\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\n\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\n/**\n * Wrap bytes in a fresh Uint8Array backed by a plain ArrayBuffer so they\n * satisfy fetch's BodyInit contract. Avoids type casts: Node's Buffer\n * type is `Buffer<ArrayBufferLike>`, while the DOM's BodyInit (used by\n * fetch's RequestInit) expects `ArrayBufferView<ArrayBuffer>`. Accepts any\n * Uint8Array (Buffer included, plus the codec's plain Uint8Array output).\n */\nfunction bufferToBody(buf: Uint8Array): Uint8Array<ArrayBuffer> {\n const out = new ArrayBuffer(buf.byteLength);\n const view = new Uint8Array(out);\n view.set(buf);\n return view;\n}\n\nfunction assertGcsSettings(\n settings: Partial<GcsStoreSettings> | undefined,\n): asserts settings is GcsStoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeGcsInit: settings.bucket is required (non-empty string)',\n );\n }\n}\n\n/**\n * Resolve a project ID for the runtime hard-fail message. Uses the same\n * resolution order as setup but never throws: returns \"unknown\" when no\n * source is available so the error message is still actionable.\n */\nfunction resolveProjectIdForMessage(\n credentials: Store.Credentials<Types> | undefined,\n): string {\n try {\n return resolveProjectId({}, credentials);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Verify the bucket exists. Issues `HEAD /storage/v1/b/<bucket>` once per\n * process per bucket. On 404, throws an actionable error pointing at the\n * setup command. On any other failure (non-2xx response or transport\n * error), treats the result as \"exists\" to avoid blocking legitimate\n * operations on a transient blip.\n */\nasync function ensureBucketExists(\n bucket: string,\n id: string,\n credentials: Store.Credentials<Types> | undefined,\n getToken: () => Promise<string>,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const existing = bucketExistsCache.get(bucket);\n if (existing !== undefined) {\n await existing;\n return;\n }\n\n const promise = (async (): Promise<boolean> => {\n let res: Response;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n res = await doFetch(url, {\n method: 'HEAD',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch (err) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n error: err instanceof Error ? err.message : String(err),\n });\n return true;\n }\n\n if (res.status === 404) {\n const projectId = resolveProjectIdForMessage(credentials);\n throw new Error(\n `GCS bucket not found: ${bucket} in project ${projectId}. Run \"walkeros setup store.${id}\" to create it.`,\n );\n }\n if (!res.ok) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n status: res.status,\n });\n }\n return true;\n })();\n\n bucketExistsCache.set(bucket, promise);\n try {\n await promise;\n } catch (err) {\n // Drop the failing entry so the next call retries the check.\n bucketExistsCache.delete(bucket);\n throw err;\n }\n}\n\nexport const storeGcsInit: Store.Init<Types> = (context) => {\n assertGcsSettings(context.config.settings);\n const settings: GcsStoreSettings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\n const rawCreds = resolveCredentials(context.config, context.logger);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included; the global fetch is the runtime default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const bucketRaw = settings.bucket;\n const bucket = encodeURIComponent(bucketRaw);\n const id = context.id;\n const { logger } = context;\n // One mode per instance, decided at init. file:true persists raw bytes\n // byte-exact with a real mime; structured mode round-trips StoreValue\n // through the shared core codec, stored as application/json.\n const fileMode = context.config.file === true;\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n const config: Store.Config<Types> = {\n settings,\n env: context.config.env,\n id: context.config.id,\n logger: context.config.logger,\n // The declared byte mode; state and validation read it from here.\n file: context.config.file,\n // `setup` from the incoming context.config is typed as `unknown` because\n // the Init context narrows only the settings/env/initSettings slots and\n // not the setup-options slot. The flow runtime never uses this field at\n // runtime (setup is invoked separately by the CLI), so omit it from the\n // returned config without a cast.\n };\n\n return {\n type: 'gcs',\n config,\n setup: gcsSetup,\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return undefined;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n let bytes: Buffer;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/download/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}?alt=media`;\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return undefined;\n bytes = Buffer.from(await res.arrayBuffer());\n } catch {\n return undefined;\n }\n // File mode hands the raw bytes back untouched (Buffer is a Uint8Array,\n // a valid StoreValue leaf). Structured mode decodes the utf8-JSON payload.\n // A corrupt or empty payload (GCS can return ok:true with an empty body)\n // degrades to a miss rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n logger.debug('structured decode failed, degrading to miss', {\n key,\n error: err instanceof Error ? err.message : String(err),\n });\n return undefined;\n }\n },\n\n async set(key: string, value: Store.StoreValue): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n let body: Uint8Array<ArrayBuffer>;\n let contentType: string;\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (typeof value === 'string') {\n body = bufferToBody(Buffer.from(value));\n } else if (value instanceof Uint8Array) {\n body = bufferToBody(value);\n } else {\n throw new Error(\n 'storeGcsInit.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n contentType = mimeFromKey(key);\n } else {\n // Structured mode: serialize to utf8-JSON bytes, uploaded as JSON.\n body = bufferToBody(serializeStoreValue(value));\n contentType = JSON_CONTENT_TYPE;\n }\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n const token = await getToken();\n const url = `${GCS_BASE}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': contentType,\n },\n body,\n });\n },\n\n async delete(key: string): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch {\n /* GCS delete is idempotent */\n }\n },\n };\n};\n","import { createSign } from 'node:crypto';\nimport type { ServiceAccountCredentials } from './types';\n\nexport type TokenProvider = () => Promise<string>;\n\nconst METADATA_URL =\n 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token';\nconst OAUTH_URL = 'https://oauth2.googleapis.com/token';\nconst SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write';\nconst REFRESH_MARGIN_MS = 60_000;\n\ninterface CachedToken {\n token: string;\n expiresAt: number;\n}\n\nexport function createTokenProvider(\n credentials?: ServiceAccountCredentials,\n doFetch: typeof fetch = fetch,\n): TokenProvider {\n let cached: CachedToken | undefined;\n\n return async function getToken(): Promise<string> {\n if (cached && Date.now() < cached.expiresAt) return cached.token;\n\n cached = credentials\n ? await fetchServiceAccountToken(credentials, doFetch)\n : await fetchMetadataToken(doFetch);\n\n return cached.token;\n };\n}\n\nasync function fetchMetadataToken(doFetch: typeof fetch): Promise<CachedToken> {\n const res = await doFetch(METADATA_URL, {\n headers: { 'Metadata-Flavor': 'Google' },\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) throw new Error(`Metadata server error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nasync function fetchServiceAccountToken(\n creds: ServiceAccountCredentials,\n doFetch: typeof fetch,\n): Promise<CachedToken> {\n const now = Math.floor(Date.now() / 1000);\n const jwt = signJwt(\n {\n iss: creds.client_email,\n scope: SCOPE,\n aud: OAUTH_URL,\n iat: now,\n exp: now + 3600,\n },\n creds.private_key,\n );\n\n const res = await doFetch(OAUTH_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,\n });\n if (!res.ok) throw new Error(`Token exchange error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nfunction base64url(input: string | Buffer): string {\n const buf = typeof input === 'string' ? Buffer.from(input) : input;\n return buf.toString('base64url');\n}\n\nfunction signJwt(payload: object, privateKey: string): string {\n const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' }));\n const body = base64url(JSON.stringify(payload));\n const sign = createSign('RSA-SHA256');\n sign.update(`${header}.${body}`);\n return `${header}.${body}.${sign.sign(privateKey, 'base64url')}`;\n}\n","import type { Logger, Store } from '@walkeros/core';\nimport type { Types } from './types';\n\ntype ResolvedCredentials = Store.Credentials<Types>;\n\n/**\n * The two credential read sites narrow different slots: `store.ts` runs inside\n * the `Store.Init` context, where the typed `credentials` slot is widened back\n * to `unknown` (settings is `Partial`); `setup.ts` runs with the full\n * `Store.Config<Types>`, where `credentials` is `ResolvedCredentials`. Accept\n * `unknown` for both and narrow at runtime.\n */\ninterface CredentialSources {\n credentials?: unknown;\n settings?: { credentials?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow an unknown credential candidate to the resolved credential shape: a\n * JSON string (often a `$env` reference) or a parsed service-account object.\n * Anything else resolves to `undefined`; the downstream parser surfaces a\n * misconfiguration rather than this layer guessing.\n */\nfunction asCredential(value: unknown): ResolvedCredentials | undefined {\n if (typeof value === 'string') return value;\n if (\n isRecord(value) &&\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n ) {\n const { client_email, private_key, project_id } = value;\n return typeof project_id === 'string'\n ? { client_email, private_key, project_id }\n : { client_email, private_key };\n }\n return undefined;\n}\n\n/**\n * Resolve the credential source for the store, preferring the strictly-typed\n * `config.credentials` slot and falling back to the deprecated\n * `settings.credentials`. Returns the raw credential (string or object); the\n * caller parses it into a service account.\n *\n * Precedence is `??` (not `||`) so an explicitly-empty `config.credentials`\n * surfaces as a misconfiguration rather than silently falling through.\n *\n * Warn-once semantics: the warning fires from the init/setup read site, which\n * runs once per store instance. No module-level flag, so a second instance on\n * the deprecated path still warns.\n */\nexport function resolveCredentials(\n config: CredentialSources,\n logger?: Logger.Instance,\n): ResolvedCredentials | undefined {\n if (config.credentials !== undefined) return asCredential(config.credentials);\n\n const fromSettings = config.settings?.credentials;\n if (fromSettings !== undefined && logger) {\n logger.warn(\n 'gcs store: settings.credentials is deprecated, use config.credentials',\n );\n }\n return asCredential(fromSettings);\n}\n","import type { LifecycleContext, Logger, Store, SetupFn } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type {\n Env,\n GcsStoreSettings,\n ServiceAccountCredentials,\n Setup,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\n/**\n * Default setup options. Optional fields (lifecycle, kmsKeyName, labels,\n * projectId) are intentionally omitted so the resolved options carry\n * `undefined` for them and the body builder can skip writing those\n * properties to the create request payload.\n */\nexport const DEFAULT_SETUP: Setup = {\n location: 'EU',\n storageClass: 'STANDARD',\n versioning: false,\n};\n\nexport interface SetupResult {\n bucketCreated: boolean;\n}\n\n/**\n * Public alias kept for callers that imported the prior shape.\n * Equivalent to the framework's `Store.Config<Types>`.\n */\nexport type GcsStoreConfig = Store.Config<Types>;\n\n/**\n * Provision a GCS bucket described in the flow config.\n * Idempotent: 409 means the bucket exists, drift is logged but never patched.\n * Never auto-mutates an existing bucket.\n */\nexport const setup: SetupFn<GcsStoreConfig, Env> = async (\n context: LifecycleContext<GcsStoreConfig, Env>,\n) => {\n const { config, logger } = context;\n const options = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!options) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n\n const settings = config.settings;\n if (!settings || !settings.bucket) {\n throw new Error('setup: settings.bucket is required');\n }\n\n const rawCreds = resolveCredentials(config, logger);\n const projectId = resolveProjectId(options, rawCreds);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included, as in the store runtime; the global fetch is the default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const token = await getToken();\n\n const createUrl = `${GCS_BASE}/storage/v1/b?project=${encodeURIComponent(projectId)}`;\n const body = buildCreateBody(settings.bucket, options);\n\n const createRes = await doFetch(createUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (createRes.status === 200 || createRes.status === 201) {\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n projectId,\n location: options.location,\n });\n return { bucketCreated: true };\n }\n\n if (createRes.status !== 409) {\n const text = await safeText(createRes);\n throw new Error(\n `setup: bucket create failed (${createRes.status}): ${text}`,\n );\n }\n\n logger.debug('setup: bucket already exists', { bucket: settings.bucket });\n await detectDrift(settings.bucket, token, options, logger, doFetch);\n return { bucketCreated: false };\n};\n\ninterface CreateBody {\n name: string;\n location?: string;\n storageClass?: string;\n versioning: { enabled: boolean };\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true };\n publicAccessPrevention: 'enforced';\n };\n labels?: Record<string, string>;\n encryption?: { defaultKmsKeyName: string };\n lifecycle?: { rule: unknown[] };\n}\n\nfunction buildCreateBody(bucket: string, options: Setup): CreateBody {\n const body: CreateBody = {\n name: bucket,\n location: options.location,\n storageClass: options.storageClass,\n versioning: { enabled: options.versioning ?? false },\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true },\n publicAccessPrevention: 'enforced',\n },\n };\n if (options.labels) body.labels = options.labels;\n if (options.kmsKeyName) {\n body.encryption = { defaultKmsKeyName: options.kmsKeyName };\n }\n if (options.lifecycle) body.lifecycle = options.lifecycle;\n return body;\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '';\n }\n}\n\ninterface BucketMetadata {\n location?: string;\n storageClass?: string;\n versioning?: { enabled?: boolean };\n iamConfiguration?: {\n uniformBucketLevelAccess?: { enabled?: boolean };\n publicAccessPrevention?: string;\n };\n labels?: Record<string, string>;\n}\n\nasync function detectDrift(\n bucket: string,\n token: string,\n options: Setup,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n let metadata: BucketMetadata;\n try {\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) {\n logger.debug('setup: drift check failed (non-fatal)', {\n status: res.status,\n });\n return;\n }\n const parsed: unknown = await res.json();\n metadata = readBucketMetadata(parsed);\n } catch (err) {\n logger.debug('setup: drift check failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n if (\n typeof metadata.location === 'string' &&\n options.location !== undefined &&\n metadata.location !== options.location\n ) {\n logger.warn('setup.drift', {\n field: 'location',\n declared: options.location,\n actual: metadata.location,\n });\n }\n\n if (\n typeof metadata.storageClass === 'string' &&\n options.storageClass !== undefined &&\n metadata.storageClass !== options.storageClass\n ) {\n logger.warn('setup.drift', {\n field: 'storageClass',\n declared: options.storageClass,\n actual: metadata.storageClass,\n });\n }\n\n const declaredVersioning = options.versioning ?? false;\n const actualVersioning = metadata.versioning?.enabled;\n if (\n typeof actualVersioning === 'boolean' &&\n actualVersioning !== declaredVersioning\n ) {\n logger.warn('setup.drift', {\n field: 'versioning',\n declared: declaredVersioning,\n actual: actualVersioning,\n });\n }\n\n const iam = metadata.iamConfiguration;\n if (iam) {\n const actualUniform = iam.uniformBucketLevelAccess?.enabled;\n if (typeof actualUniform === 'boolean' && actualUniform !== true) {\n logger.warn('setup.drift', {\n field: 'uniformBucketLevelAccess',\n declared: true,\n actual: actualUniform,\n });\n }\n const actualPap = iam.publicAccessPrevention;\n if (typeof actualPap === 'string' && actualPap !== 'enforced') {\n logger.warn('setup.drift', {\n field: 'publicAccessPrevention',\n declared: 'enforced',\n actual: actualPap,\n });\n }\n }\n\n if (options.labels) {\n const actualLabels = metadata.labels ?? {};\n if (!labelsEqual(options.labels, actualLabels)) {\n logger.warn('setup.drift', {\n field: 'labels',\n declared: options.labels,\n actual: actualLabels,\n });\n }\n }\n}\n\nfunction labelsEqual(\n a: Record<string, string>,\n b: Record<string, string>,\n): boolean {\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (a[key] !== b[key]) return false;\n }\n return true;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readBucketMetadata(value: unknown): BucketMetadata {\n if (!isRecord(value)) return {};\n const out: BucketMetadata = {};\n if (typeof value.location === 'string') out.location = value.location;\n if (typeof value.storageClass === 'string') {\n out.storageClass = value.storageClass;\n }\n if (isRecord(value.versioning)) {\n const enabled = value.versioning.enabled;\n if (typeof enabled === 'boolean') {\n out.versioning = { enabled };\n }\n }\n if (isRecord(value.iamConfiguration)) {\n const iam: BucketMetadata['iamConfiguration'] = {};\n const uniform = value.iamConfiguration.uniformBucketLevelAccess;\n if (isRecord(uniform) && typeof uniform.enabled === 'boolean') {\n iam.uniformBucketLevelAccess = { enabled: uniform.enabled };\n }\n const pap = value.iamConfiguration.publicAccessPrevention;\n if (typeof pap === 'string') iam.publicAccessPrevention = pap;\n out.iamConfiguration = iam;\n }\n if (isRecord(value.labels)) {\n const labels: Record<string, string> = {};\n for (const [k, v] of Object.entries(value.labels)) {\n if (typeof v === 'string') labels[k] = v;\n }\n out.labels = labels;\n }\n return out;\n}\n","import type { Store } from '@walkeros/core';\nimport type { ServiceAccountCredentials, Types } from './types';\n\n/**\n * Resolves the GCP project ID used to create a bucket in the GCS JSON API.\n *\n * Resolution order:\n * 1. `setup.projectId` (explicit override).\n * 2. the resolved credentials' `project_id` (when SA JSON is parsed and contains it).\n * 3. `process.env.GOOGLE_CLOUD_PROJECT` (Cloud Run / GKE convention).\n * 4. Throw with an actionable error message.\n *\n * `process.env.GOOGLE_CLOUD_PROJECT` is read at call time, not at module load,\n * so changes after import are honored.\n *\n * `credentials` is the already-resolved value (`config.credentials ??\n * settings.credentials`), not read off settings here, so `config.credentials`\n * is honored for project-id derivation.\n */\nexport function resolveProjectId(\n setup: { projectId?: string },\n credentials: Store.Credentials<Types> | undefined,\n): string {\n if (setup.projectId) return setup.projectId;\n\n const fromCreds = extractProjectIdFromCredentials(credentials);\n if (fromCreds) return fromCreds;\n\n const fromEnv = process.env.GOOGLE_CLOUD_PROJECT;\n if (fromEnv) return fromEnv;\n\n throw new Error(\n 'setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.',\n );\n}\n\n/**\n * Typed predicate: returns the `project_id` field from credentials when\n * present and a string. Avoids type casts by validating the runtime shape.\n *\n * - `string` credentials are parsed as JSON; non-JSON or non-object payloads\n * return undefined.\n * - object credentials are inspected directly. Real SA JSON contains\n * `project_id` even though `ServiceAccountCredentials` declares only the\n * fields the runtime needs (client_email, private_key).\n */\nfunction extractProjectIdFromCredentials(\n credentials: Store.Credentials<Types> | undefined,\n): string | undefined {\n if (!credentials) return undefined;\n\n if (typeof credentials === 'string') {\n let parsed: unknown;\n try {\n parsed = JSON.parse(credentials);\n } catch {\n return undefined;\n }\n return readStringField(parsed, 'project_id');\n }\n\n return readStringField(credentials, 'project_id');\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readStringField(\n value: ServiceAccountCredentials | unknown,\n field: string,\n): string | undefined {\n if (!isRecord(value)) return undefined;\n const fieldValue = value[field];\n return typeof fieldValue === 'string' ? fieldValue : undefined;\n}\n"],"mappings":"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","names":["import_core","setup","isRecord","isRecord","GCS_BASE","isRecord","isServiceAccountShape","parseCredentials","GCS_BASE"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{serializeStoreValue as e,deserializeStoreValue as t}from"@walkeros/core";import{createSign as n}from"crypto";var r="http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token",o="https://oauth2.googleapis.com/token",i="https://www.googleapis.com/auth/devstorage.read_write",a=6e4;function s(e,t=fetch){let s;return async function(){return s&&Date.now()<s.expiresAt||(s=e?await async function(e,t){const r=Math.floor(Date.now()/1e3),s=function(e,t){const r=c(JSON.stringify({alg:"RS256",typ:"JWT"})),o=c(JSON.stringify(e)),i=n("RSA-SHA256");return i.update(`${r}.${o}`),`${r}.${o}.${i.sign(t,"base64url")}`}({iss:e.client_email,scope:i,aud:o,iat:r,exp:r+3600},e.private_key),u=await t(o,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${s}`});if(!u.ok)throw new Error(`Token exchange error: ${u.status}`);const f=await u.json();return{token:f.access_token,expiresAt:Date.now()+1e3*f.expires_in-a}}(e,t):await async function(e){const t=await e(r,{headers:{"Metadata-Flavor":"Google"},signal:AbortSignal.timeout(5e3)});if(!t.ok)throw new Error(`Metadata server error: ${t.status}`);const n=await t.json();return{token:n.access_token,expiresAt:Date.now()+1e3*n.expires_in-a}}(t)),s.token}}function c(e){return("string"==typeof e?Buffer.from(e):e).toString("base64url")}function u(e){if("string"==typeof e)return e;if(function(e){return"object"==typeof e&&null!==e}(e)&&"string"==typeof e.client_email&&"string"==typeof e.private_key){const{client_email:t,private_key:n,project_id:r}=e;return"string"==typeof r?{client_email:t,private_key:n,project_id:r}:{client_email:t,private_key:n}}}function f(e,t){if(void 0!==e.credentials)return u(e.credentials);const n=e.settings?.credentials;return void 0!==n&&t&&t.warn("gcs store: settings.credentials is deprecated, use config.credentials"),u(n)}import{resolveSetup as l}from"@walkeros/core";function p(e,t){if(e.projectId)return e.projectId;const n=function(e){if(!e)return;if("string"==typeof e){let t;try{t=JSON.parse(e)}catch{return}return g(t,"project_id")}return g(e,"project_id")}(t);if(n)return n;const r=process.env.GOOGLE_CLOUD_PROJECT;if(r)return r;throw new Error("setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.")}function g(e,t){if(!function(e){return"object"==typeof e&&null!==e}(e))return;const n=e[t];return"string"==typeof n?n:void 0}var d="https://storage.googleapis.com",y={location:"EU",storageClass:"STANDARD",versioning:!1},b=async e=>{const{config:t,logger:n}=e,r=l(t.setup,y);if(!r)return void n.debug("setup: skipped (config.setup is false or unset)");const o=t.settings;if(!o||!o.bucket)throw new Error("setup: settings.bucket is required");const i=f(t,n),a=p(r,i),c=function(e){if(!e)return;if("string"==typeof e){const n=JSON.parse(e);return m(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}(i),u=e.env?.fetch??e.config.env?.fetch??fetch,g=s(c,u),b=await g(),w=`${d}/storage/v1/b?project=${encodeURIComponent(a)}`,v=function(e,t){const n={name:e,location:t.location,storageClass:t.storageClass,versioning:{enabled:t.versioning??!1},iamConfiguration:{uniformBucketLevelAccess:{enabled:!0},publicAccessPrevention:"enforced"}};t.labels&&(n.labels=t.labels);t.kmsKeyName&&(n.encryption={defaultKmsKeyName:t.kmsKeyName});t.lifecycle&&(n.lifecycle=t.lifecycle);return n}(o.bucket,r),h=await u(w,{method:"POST",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"},body:JSON.stringify(v)});if(200===h.status||201===h.status)return n.info("setup: bucket created",{bucket:o.bucket,projectId:a,location:r.location}),{bucketCreated:!0};if(409!==h.status){const e=await async function(e){try{return await e.text()}catch{return""}}(h);throw new Error(`setup: bucket create failed (${h.status}): ${e}`)}return n.debug("setup: bucket already exists",{bucket:o.bucket}),await async function(e,t,n,r,o){const i=`${d}/storage/v1/b/${encodeURIComponent(e)}`;let a;try{const e=await o(i,{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)return void r.debug("setup: drift check failed (non-fatal)",{status:e.status});a=function(e){if(!m(e))return{};const t={};"string"==typeof e.location&&(t.location=e.location);"string"==typeof e.storageClass&&(t.storageClass=e.storageClass);if(m(e.versioning)){const n=e.versioning.enabled;"boolean"==typeof n&&(t.versioning={enabled:n})}if(m(e.iamConfiguration)){const n={},r=e.iamConfiguration.uniformBucketLevelAccess;m(r)&&"boolean"==typeof r.enabled&&(n.uniformBucketLevelAccess={enabled:r.enabled});const o=e.iamConfiguration.publicAccessPrevention;"string"==typeof o&&(n.publicAccessPrevention=o),t.iamConfiguration=n}if(m(e.labels)){const n={};for(const[t,r]of Object.entries(e.labels))"string"==typeof r&&(n[t]=r);t.labels=n}return t}(await e.json())}catch(e){return void r.debug("setup: drift check failed (non-fatal)",{error:e instanceof Error?e.message:String(e)})}"string"==typeof a.location&&void 0!==n.location&&a.location!==n.location&&r.warn("setup.drift",{field:"location",declared:n.location,actual:a.location});"string"==typeof a.storageClass&&void 0!==n.storageClass&&a.storageClass!==n.storageClass&&r.warn("setup.drift",{field:"storageClass",declared:n.storageClass,actual:a.storageClass});const s=n.versioning??!1,c=a.versioning?.enabled;"boolean"==typeof c&&c!==s&&r.warn("setup.drift",{field:"versioning",declared:s,actual:c});const u=a.iamConfiguration;if(u){const e=u.uniformBucketLevelAccess?.enabled;"boolean"==typeof e&&!0!==e&&r.warn("setup.drift",{field:"uniformBucketLevelAccess",declared:!0,actual:e});const t=u.publicAccessPrevention;"string"==typeof t&&"enforced"!==t&&r.warn("setup.drift",{field:"publicAccessPrevention",declared:"enforced",actual:t})}if(n.labels){const e=a.labels??{};(function(e,t){const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(const r of n)if(e[r]!==t[r])return!1;return!0})(n.labels,e)||r.warn("setup.drift",{field:"labels",declared:n.labels,actual:e})}}(o.bucket,b,r,n,u),{bucketCreated:!1}};function m(e){return"object"==typeof e&&null!==e}var w="https://storage.googleapis.com",v="application/octet-stream",h={js:"application/javascript",mjs:"application/javascript",json:"application/json",css:"text/css",html:"text/html",txt:"text/plain",svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",ico:"image/x-icon",wasm:"application/wasm",map:"application/json"};var k=new Map;function j(e){const t=new ArrayBuffer(e.byteLength),n=new Uint8Array(t);return n.set(e),n}async function C(e,t,n,r,o,i){const a=k.get(e);if(void 0!==a)return void await a;const s=(async()=>{let a;try{const t=await r(),n=`${w}/storage/v1/b/${encodeURIComponent(e)}`;a=await i(n,{method:"HEAD",headers:{Authorization:`Bearer ${t}`}})}catch(t){return o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,error:t instanceof Error?t.message:String(t)}),!0}if(404===a.status){const r=function(e){try{return p({},e)}catch{return"unknown"}}(n);throw new Error(`GCS bucket not found: ${e} in project ${r}. Run "walkeros setup store.${t}" to create it.`)}return a.ok||o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,status:a.status}),!0})();k.set(e,s);try{await s}catch(t){throw k.delete(e),t}}var $=n=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeGcsInit: settings.bucket is required (non-empty string)")}(n.config.settings);const r=n.config.settings,o=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(r.prefix),i=f(n.config,n.logger),a=function(e){if(e){if("string"==typeof e){const n=JSON.parse(e);return function(e){return"object"==typeof e&&null!==e}(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}}(i),c=n.env?.fetch??n.config.env?.fetch??fetch,u=s(a,c),l=r.bucket,p=encodeURIComponent(l),g=n.id,{logger:d}=n,y=!0===n.config.file;function m(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return o+e;d.warn("Invalid key rejected",{key:e})}return{type:"gcs",config:{settings:r,env:n.config.env,id:n.config.id,logger:n.config.logger},setup:b,async get(e){const n=m(e);if(!n)return;let r;await C(l,g,i,u,d,c);try{const e=await u(),t=`${w}/download/storage/v1/b/${p}/o/${encodeURIComponent(n)}?alt=media`,o=await c(t,{headers:{Authorization:`Bearer ${e}`}});if(!o.ok)return;r=Buffer.from(await o.arrayBuffer())}catch{return}if(y)return r;try{return t(r)}catch(t){return void d.debug("structured decode failed, degrading to miss",{key:e,error:t instanceof Error?t.message:String(t)})}},async set(t,n){const r=m(t);if(!r)return;let o,a;if(y){if("string"==typeof n)o=j(Buffer.from(n));else{if(!(n instanceof Uint8Array))throw new Error("storeGcsInit.set: in file mode value must be Uint8Array or string; got "+typeof n);o=j(n)}a=function(e){const t=e.lastIndexOf(".");if(t<0||t===e.length-1)return v;const n=e.slice(t+1).toLowerCase();return h[n]??v}(t)}else o=j(e(n)),a="application/json";await C(l,g,i,u,d,c);const s=await u(),f=`${w}/upload/storage/v1/b/${p}/o?uploadType=media&name=${encodeURIComponent(r)}`;await c(f,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":a},body:o})},async delete(e){const t=m(e);if(t){await C(l,g,i,u,d,c);try{const e=await u(),n=`${w}/storage/v1/b/${p}/o/${encodeURIComponent(t)}`;await c(n,{method:"DELETE",headers:{Authorization:`Bearer ${e}`}})}catch{}}}}};export{y as DEFAULT_SETUP,$ as default,b as setup,$ as storeGcsInit};//# sourceMappingURL=index.mjs.map
|
|
1
|
+
import{serializeStoreValue as e,deserializeStoreValue as t}from"@walkeros/core";import{createSign as n}from"crypto";var r="http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token",o="https://oauth2.googleapis.com/token",i="https://www.googleapis.com/auth/devstorage.read_write",a=6e4;function s(e,t=fetch){let s;return async function(){return s&&Date.now()<s.expiresAt||(s=e?await async function(e,t){const r=Math.floor(Date.now()/1e3),s=function(e,t){const r=c(JSON.stringify({alg:"RS256",typ:"JWT"})),o=c(JSON.stringify(e)),i=n("RSA-SHA256");return i.update(`${r}.${o}`),`${r}.${o}.${i.sign(t,"base64url")}`}({iss:e.client_email,scope:i,aud:o,iat:r,exp:r+3600},e.private_key),u=await t(o,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${s}`});if(!u.ok)throw new Error(`Token exchange error: ${u.status}`);const f=await u.json();return{token:f.access_token,expiresAt:Date.now()+1e3*f.expires_in-a}}(e,t):await async function(e){const t=await e(r,{headers:{"Metadata-Flavor":"Google"},signal:AbortSignal.timeout(5e3)});if(!t.ok)throw new Error(`Metadata server error: ${t.status}`);const n=await t.json();return{token:n.access_token,expiresAt:Date.now()+1e3*n.expires_in-a}}(t)),s.token}}function c(e){return("string"==typeof e?Buffer.from(e):e).toString("base64url")}function u(e){if("string"==typeof e)return e;if(function(e){return"object"==typeof e&&null!==e}(e)&&"string"==typeof e.client_email&&"string"==typeof e.private_key){const{client_email:t,private_key:n,project_id:r}=e;return"string"==typeof r?{client_email:t,private_key:n,project_id:r}:{client_email:t,private_key:n}}}function f(e,t){if(void 0!==e.credentials)return u(e.credentials);const n=e.settings?.credentials;return void 0!==n&&t&&t.warn("gcs store: settings.credentials is deprecated, use config.credentials"),u(n)}import{resolveSetup as l}from"@walkeros/core";function p(e,t){if(e.projectId)return e.projectId;const n=function(e){if(!e)return;if("string"==typeof e){let t;try{t=JSON.parse(e)}catch{return}return g(t,"project_id")}return g(e,"project_id")}(t);if(n)return n;const r=process.env.GOOGLE_CLOUD_PROJECT;if(r)return r;throw new Error("setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.")}function g(e,t){if(!function(e){return"object"==typeof e&&null!==e}(e))return;const n=e[t];return"string"==typeof n?n:void 0}var d="https://storage.googleapis.com",y={location:"EU",storageClass:"STANDARD",versioning:!1},b=async e=>{const{config:t,logger:n}=e,r=l(t.setup,y);if(!r)return void n.debug("setup: skipped (config.setup is false or unset)");const o=t.settings;if(!o||!o.bucket)throw new Error("setup: settings.bucket is required");const i=f(t,n),a=p(r,i),c=function(e){if(!e)return;if("string"==typeof e){const n=JSON.parse(e);return m(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}(i),u=e.env?.fetch??e.config.env?.fetch??fetch,g=s(c,u),b=await g(),w=`${d}/storage/v1/b?project=${encodeURIComponent(a)}`,v=function(e,t){const n={name:e,location:t.location,storageClass:t.storageClass,versioning:{enabled:t.versioning??!1},iamConfiguration:{uniformBucketLevelAccess:{enabled:!0},publicAccessPrevention:"enforced"}};t.labels&&(n.labels=t.labels);t.kmsKeyName&&(n.encryption={defaultKmsKeyName:t.kmsKeyName});t.lifecycle&&(n.lifecycle=t.lifecycle);return n}(o.bucket,r),h=await u(w,{method:"POST",headers:{Authorization:`Bearer ${b}`,"Content-Type":"application/json"},body:JSON.stringify(v)});if(200===h.status||201===h.status)return n.info("setup: bucket created",{bucket:o.bucket,projectId:a,location:r.location}),{bucketCreated:!0};if(409!==h.status){const e=await async function(e){try{return await e.text()}catch{return""}}(h);throw new Error(`setup: bucket create failed (${h.status}): ${e}`)}return n.debug("setup: bucket already exists",{bucket:o.bucket}),await async function(e,t,n,r,o){const i=`${d}/storage/v1/b/${encodeURIComponent(e)}`;let a;try{const e=await o(i,{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)return void r.debug("setup: drift check failed (non-fatal)",{status:e.status});a=function(e){if(!m(e))return{};const t={};"string"==typeof e.location&&(t.location=e.location);"string"==typeof e.storageClass&&(t.storageClass=e.storageClass);if(m(e.versioning)){const n=e.versioning.enabled;"boolean"==typeof n&&(t.versioning={enabled:n})}if(m(e.iamConfiguration)){const n={},r=e.iamConfiguration.uniformBucketLevelAccess;m(r)&&"boolean"==typeof r.enabled&&(n.uniformBucketLevelAccess={enabled:r.enabled});const o=e.iamConfiguration.publicAccessPrevention;"string"==typeof o&&(n.publicAccessPrevention=o),t.iamConfiguration=n}if(m(e.labels)){const n={};for(const[t,r]of Object.entries(e.labels))"string"==typeof r&&(n[t]=r);t.labels=n}return t}(await e.json())}catch(e){return void r.debug("setup: drift check failed (non-fatal)",{error:e instanceof Error?e.message:String(e)})}"string"==typeof a.location&&void 0!==n.location&&a.location!==n.location&&r.warn("setup.drift",{field:"location",declared:n.location,actual:a.location});"string"==typeof a.storageClass&&void 0!==n.storageClass&&a.storageClass!==n.storageClass&&r.warn("setup.drift",{field:"storageClass",declared:n.storageClass,actual:a.storageClass});const s=n.versioning??!1,c=a.versioning?.enabled;"boolean"==typeof c&&c!==s&&r.warn("setup.drift",{field:"versioning",declared:s,actual:c});const u=a.iamConfiguration;if(u){const e=u.uniformBucketLevelAccess?.enabled;"boolean"==typeof e&&!0!==e&&r.warn("setup.drift",{field:"uniformBucketLevelAccess",declared:!0,actual:e});const t=u.publicAccessPrevention;"string"==typeof t&&"enforced"!==t&&r.warn("setup.drift",{field:"publicAccessPrevention",declared:"enforced",actual:t})}if(n.labels){const e=a.labels??{};(function(e,t){const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(const r of n)if(e[r]!==t[r])return!1;return!0})(n.labels,e)||r.warn("setup.drift",{field:"labels",declared:n.labels,actual:e})}}(o.bucket,b,r,n,u),{bucketCreated:!1}};function m(e){return"object"==typeof e&&null!==e}var w="https://storage.googleapis.com",v="application/octet-stream",h={js:"application/javascript",mjs:"application/javascript",json:"application/json",css:"text/css",html:"text/html",txt:"text/plain",svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",ico:"image/x-icon",wasm:"application/wasm",map:"application/json"};var k=new Map;function j(e){const t=new ArrayBuffer(e.byteLength),n=new Uint8Array(t);return n.set(e),n}async function C(e,t,n,r,o,i){const a=k.get(e);if(void 0!==a)return void await a;const s=(async()=>{let a;try{const t=await r(),n=`${w}/storage/v1/b/${encodeURIComponent(e)}`;a=await i(n,{method:"HEAD",headers:{Authorization:`Bearer ${t}`}})}catch(t){return o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,error:t instanceof Error?t.message:String(t)}),!0}if(404===a.status){const r=function(e){try{return p({},e)}catch{return"unknown"}}(n);throw new Error(`GCS bucket not found: ${e} in project ${r}. Run "walkeros setup store.${t}" to create it.`)}return a.ok||o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,status:a.status}),!0})();k.set(e,s);try{await s}catch(t){throw k.delete(e),t}}var $=n=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeGcsInit: settings.bucket is required (non-empty string)")}(n.config.settings);const r=n.config.settings,o=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(r.prefix),i=f(n.config,n.logger),a=function(e){if(e){if("string"==typeof e){const n=JSON.parse(e);return function(e){return"object"==typeof e&&null!==e}(t=n)&&"string"==typeof t.client_email&&"string"==typeof t.private_key?n:void 0}var t;return e}}(i),c=n.env?.fetch??n.config.env?.fetch??fetch,u=s(a,c),l=r.bucket,p=encodeURIComponent(l),g=n.id,{logger:d}=n,y=!0===n.config.file;function m(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return o+e;d.warn("Invalid key rejected",{key:e})}return{type:"gcs",config:{settings:r,env:n.config.env,id:n.config.id,logger:n.config.logger,file:n.config.file},setup:b,async get(e){const n=m(e);if(!n)return;let r;await C(l,g,i,u,d,c);try{const e=await u(),t=`${w}/download/storage/v1/b/${p}/o/${encodeURIComponent(n)}?alt=media`,o=await c(t,{headers:{Authorization:`Bearer ${e}`}});if(!o.ok)return;r=Buffer.from(await o.arrayBuffer())}catch{return}if(y)return r;try{return t(r)}catch(t){return void d.debug("structured decode failed, degrading to miss",{key:e,error:t instanceof Error?t.message:String(t)})}},async set(t,n){const r=m(t);if(!r)return;let o,a;if(y){if("string"==typeof n)o=j(Buffer.from(n));else{if(!(n instanceof Uint8Array))throw new Error("storeGcsInit.set: in file mode value must be Uint8Array or string; got "+typeof n);o=j(n)}a=function(e){const t=e.lastIndexOf(".");if(t<0||t===e.length-1)return v;const n=e.slice(t+1).toLowerCase();return h[n]??v}(t)}else o=j(e(n)),a="application/json";await C(l,g,i,u,d,c);const s=await u(),f=`${w}/upload/storage/v1/b/${p}/o?uploadType=media&name=${encodeURIComponent(r)}`;await c(f,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":a},body:o})},async delete(e){const t=m(e);if(t){await C(l,g,i,u,d,c);try{const e=await u(),n=`${w}/storage/v1/b/${p}/o/${encodeURIComponent(t)}`;await c(n,{method:"DELETE",headers:{Authorization:`Bearer ${e}`}})}catch{}}}}};export{y as DEFAULT_SETUP,$ as default,b as setup,$ as storeGcsInit};//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/auth.ts","../src/credentials.ts","../src/setup.ts","../src/setup-helpers.ts"],"sourcesContent":["import type { Logger, Store } from '@walkeros/core';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type {\n GcsStoreSettings,\n ServiceAccountCredentials,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { setup as gcsSetup } from './setup';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\nconst JSON_CONTENT_TYPE = 'application/json';\nconst DEFAULT_CONTENT_TYPE = 'application/octet-stream';\n\n// Minimal extension to content-type map for the file-mode asset-serving use\n// case (serving walker.js and friends back with a correct mime). Anything not\n// listed falls back to application/octet-stream.\nconst MIME_BY_EXT: Readonly<Record<string, string>> = {\n js: 'application/javascript',\n mjs: 'application/javascript',\n json: 'application/json',\n css: 'text/css',\n html: 'text/html',\n txt: 'text/plain',\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n ico: 'image/x-icon',\n wasm: 'application/wasm',\n map: 'application/json',\n};\n\nfunction mimeFromKey(key: string): string {\n const dot = key.lastIndexOf('.');\n if (dot < 0 || dot === key.length - 1) return DEFAULT_CONTENT_TYPE;\n const ext = key.slice(dot + 1).toLowerCase();\n return MIME_BY_EXT[ext] ?? DEFAULT_CONTENT_TYPE;\n}\n\n/**\n * Module-level cache for the bucket existence pre-check. Keyed by bucket\n * name (bucket names are globally unique in GCS). One HEAD per process per\n * bucket. The cache stores a Promise so concurrent first-operations share a\n * single in-flight request.\n *\n * Exported helpers below allow tests to reset/seed the cache.\n */\nconst bucketExistsCache: Map<string, Promise<boolean>> = new Map();\n\n/** @internal Test-only: clear the existence cache. */\nexport function __resetBucketExistenceCache(): void {\n bucketExistsCache.clear();\n}\n\n/** @internal Test-only: seed the existence cache for a bucket. */\nexport function __seedBucketExists(bucket: string): void {\n bucketExistsCache.set(bucket, Promise.resolve(true));\n}\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\n\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\n/**\n * Wrap bytes in a fresh Uint8Array backed by a plain ArrayBuffer so they\n * satisfy fetch's BodyInit contract. Avoids type casts: Node's Buffer\n * type is `Buffer<ArrayBufferLike>`, while the DOM's BodyInit (used by\n * fetch's RequestInit) expects `ArrayBufferView<ArrayBuffer>`. Accepts any\n * Uint8Array (Buffer included, plus the codec's plain Uint8Array output).\n */\nfunction bufferToBody(buf: Uint8Array): Uint8Array<ArrayBuffer> {\n const out = new ArrayBuffer(buf.byteLength);\n const view = new Uint8Array(out);\n view.set(buf);\n return view;\n}\n\nfunction assertGcsSettings(\n settings: Partial<GcsStoreSettings> | undefined,\n): asserts settings is GcsStoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeGcsInit: settings.bucket is required (non-empty string)',\n );\n }\n}\n\n/**\n * Resolve a project ID for the runtime hard-fail message. Uses the same\n * resolution order as setup but never throws: returns \"unknown\" when no\n * source is available so the error message is still actionable.\n */\nfunction resolveProjectIdForMessage(\n credentials: Store.Credentials<Types> | undefined,\n): string {\n try {\n return resolveProjectId({}, credentials);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Verify the bucket exists. Issues `HEAD /storage/v1/b/<bucket>` once per\n * process per bucket. On 404, throws an actionable error pointing at the\n * setup command. On any other failure (non-2xx response or transport\n * error), treats the result as \"exists\" to avoid blocking legitimate\n * operations on a transient blip.\n */\nasync function ensureBucketExists(\n bucket: string,\n id: string,\n credentials: Store.Credentials<Types> | undefined,\n getToken: () => Promise<string>,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const existing = bucketExistsCache.get(bucket);\n if (existing !== undefined) {\n await existing;\n return;\n }\n\n const promise = (async (): Promise<boolean> => {\n let res: Response;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n res = await doFetch(url, {\n method: 'HEAD',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch (err) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n error: err instanceof Error ? err.message : String(err),\n });\n return true;\n }\n\n if (res.status === 404) {\n const projectId = resolveProjectIdForMessage(credentials);\n throw new Error(\n `GCS bucket not found: ${bucket} in project ${projectId}. Run \"walkeros setup store.${id}\" to create it.`,\n );\n }\n if (!res.ok) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n status: res.status,\n });\n }\n return true;\n })();\n\n bucketExistsCache.set(bucket, promise);\n try {\n await promise;\n } catch (err) {\n // Drop the failing entry so the next call retries the check.\n bucketExistsCache.delete(bucket);\n throw err;\n }\n}\n\nexport const storeGcsInit: Store.Init<Types> = (context) => {\n assertGcsSettings(context.config.settings);\n const settings: GcsStoreSettings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\n const rawCreds = resolveCredentials(context.config, context.logger);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included; the global fetch is the runtime default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const bucketRaw = settings.bucket;\n const bucket = encodeURIComponent(bucketRaw);\n const id = context.id;\n const { logger } = context;\n // One mode per instance, decided at init. file:true persists raw bytes\n // byte-exact with a real mime; structured mode round-trips StoreValue\n // through the shared core codec, stored as application/json.\n const fileMode = context.config.file === true;\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n const config: Store.Config<Types> = {\n settings,\n env: context.config.env,\n id: context.config.id,\n logger: context.config.logger,\n // `setup` from the incoming context.config is typed as `unknown` because\n // the Init context narrows only the settings/env/initSettings slots and\n // not the setup-options slot. The flow runtime never uses this field at\n // runtime (setup is invoked separately by the CLI), so omit it from the\n // returned config without a cast.\n };\n\n return {\n type: 'gcs',\n config,\n setup: gcsSetup,\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return undefined;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n let bytes: Buffer;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/download/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}?alt=media`;\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return undefined;\n bytes = Buffer.from(await res.arrayBuffer());\n } catch {\n return undefined;\n }\n // File mode hands the raw bytes back untouched (Buffer is a Uint8Array,\n // a valid StoreValue leaf). Structured mode decodes the utf8-JSON payload.\n // A corrupt or empty payload (GCS can return ok:true with an empty body)\n // degrades to a miss rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n logger.debug('structured decode failed, degrading to miss', {\n key,\n error: err instanceof Error ? err.message : String(err),\n });\n return undefined;\n }\n },\n\n async set(key: string, value: Store.StoreValue): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n let body: Uint8Array<ArrayBuffer>;\n let contentType: string;\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (typeof value === 'string') {\n body = bufferToBody(Buffer.from(value));\n } else if (value instanceof Uint8Array) {\n body = bufferToBody(value);\n } else {\n throw new Error(\n 'storeGcsInit.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n contentType = mimeFromKey(key);\n } else {\n // Structured mode: serialize to utf8-JSON bytes, uploaded as JSON.\n body = bufferToBody(serializeStoreValue(value));\n contentType = JSON_CONTENT_TYPE;\n }\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n const token = await getToken();\n const url = `${GCS_BASE}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': contentType,\n },\n body,\n });\n },\n\n async delete(key: string): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch {\n /* GCS delete is idempotent */\n }\n },\n };\n};\n","import { createSign } from 'node:crypto';\nimport type { ServiceAccountCredentials } from './types';\n\nexport type TokenProvider = () => Promise<string>;\n\nconst METADATA_URL =\n 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token';\nconst OAUTH_URL = 'https://oauth2.googleapis.com/token';\nconst SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write';\nconst REFRESH_MARGIN_MS = 60_000;\n\ninterface CachedToken {\n token: string;\n expiresAt: number;\n}\n\nexport function createTokenProvider(\n credentials?: ServiceAccountCredentials,\n doFetch: typeof fetch = fetch,\n): TokenProvider {\n let cached: CachedToken | undefined;\n\n return async function getToken(): Promise<string> {\n if (cached && Date.now() < cached.expiresAt) return cached.token;\n\n cached = credentials\n ? await fetchServiceAccountToken(credentials, doFetch)\n : await fetchMetadataToken(doFetch);\n\n return cached.token;\n };\n}\n\nasync function fetchMetadataToken(doFetch: typeof fetch): Promise<CachedToken> {\n const res = await doFetch(METADATA_URL, {\n headers: { 'Metadata-Flavor': 'Google' },\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) throw new Error(`Metadata server error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nasync function fetchServiceAccountToken(\n creds: ServiceAccountCredentials,\n doFetch: typeof fetch,\n): Promise<CachedToken> {\n const now = Math.floor(Date.now() / 1000);\n const jwt = signJwt(\n {\n iss: creds.client_email,\n scope: SCOPE,\n aud: OAUTH_URL,\n iat: now,\n exp: now + 3600,\n },\n creds.private_key,\n );\n\n const res = await doFetch(OAUTH_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,\n });\n if (!res.ok) throw new Error(`Token exchange error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nfunction base64url(input: string | Buffer): string {\n const buf = typeof input === 'string' ? Buffer.from(input) : input;\n return buf.toString('base64url');\n}\n\nfunction signJwt(payload: object, privateKey: string): string {\n const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' }));\n const body = base64url(JSON.stringify(payload));\n const sign = createSign('RSA-SHA256');\n sign.update(`${header}.${body}`);\n return `${header}.${body}.${sign.sign(privateKey, 'base64url')}`;\n}\n","import type { Logger, Store } from '@walkeros/core';\nimport type { Types } from './types';\n\ntype ResolvedCredentials = Store.Credentials<Types>;\n\n/**\n * The two credential read sites narrow different slots: `store.ts` runs inside\n * the `Store.Init` context, where the typed `credentials` slot is widened back\n * to `unknown` (settings is `Partial`); `setup.ts` runs with the full\n * `Store.Config<Types>`, where `credentials` is `ResolvedCredentials`. Accept\n * `unknown` for both and narrow at runtime.\n */\ninterface CredentialSources {\n credentials?: unknown;\n settings?: { credentials?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow an unknown credential candidate to the resolved credential shape: a\n * JSON string (often a `$env` reference) or a parsed service-account object.\n * Anything else resolves to `undefined`; the downstream parser surfaces a\n * misconfiguration rather than this layer guessing.\n */\nfunction asCredential(value: unknown): ResolvedCredentials | undefined {\n if (typeof value === 'string') return value;\n if (\n isRecord(value) &&\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n ) {\n const { client_email, private_key, project_id } = value;\n return typeof project_id === 'string'\n ? { client_email, private_key, project_id }\n : { client_email, private_key };\n }\n return undefined;\n}\n\n/**\n * Resolve the credential source for the store, preferring the strictly-typed\n * `config.credentials` slot and falling back to the deprecated\n * `settings.credentials`. Returns the raw credential (string or object); the\n * caller parses it into a service account.\n *\n * Precedence is `??` (not `||`) so an explicitly-empty `config.credentials`\n * surfaces as a misconfiguration rather than silently falling through.\n *\n * Warn-once semantics: the warning fires from the init/setup read site, which\n * runs once per store instance. No module-level flag, so a second instance on\n * the deprecated path still warns.\n */\nexport function resolveCredentials(\n config: CredentialSources,\n logger?: Logger.Instance,\n): ResolvedCredentials | undefined {\n if (config.credentials !== undefined) return asCredential(config.credentials);\n\n const fromSettings = config.settings?.credentials;\n if (fromSettings !== undefined && logger) {\n logger.warn(\n 'gcs store: settings.credentials is deprecated, use config.credentials',\n );\n }\n return asCredential(fromSettings);\n}\n","import type { LifecycleContext, Logger, Store, SetupFn } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type {\n Env,\n GcsStoreSettings,\n ServiceAccountCredentials,\n Setup,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\n/**\n * Default setup options. Optional fields (lifecycle, kmsKeyName, labels,\n * projectId) are intentionally omitted so the resolved options carry\n * `undefined` for them and the body builder can skip writing those\n * properties to the create request payload.\n */\nexport const DEFAULT_SETUP: Setup = {\n location: 'EU',\n storageClass: 'STANDARD',\n versioning: false,\n};\n\nexport interface SetupResult {\n bucketCreated: boolean;\n}\n\n/**\n * Public alias kept for callers that imported the prior shape.\n * Equivalent to the framework's `Store.Config<Types>`.\n */\nexport type GcsStoreConfig = Store.Config<Types>;\n\n/**\n * Provision a GCS bucket described in the flow config.\n * Idempotent: 409 means the bucket exists, drift is logged but never patched.\n * Never auto-mutates an existing bucket.\n */\nexport const setup: SetupFn<GcsStoreConfig, Env> = async (\n context: LifecycleContext<GcsStoreConfig, Env>,\n) => {\n const { config, logger } = context;\n const options = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!options) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n\n const settings = config.settings;\n if (!settings || !settings.bucket) {\n throw new Error('setup: settings.bucket is required');\n }\n\n const rawCreds = resolveCredentials(config, logger);\n const projectId = resolveProjectId(options, rawCreds);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included, as in the store runtime; the global fetch is the default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const token = await getToken();\n\n const createUrl = `${GCS_BASE}/storage/v1/b?project=${encodeURIComponent(projectId)}`;\n const body = buildCreateBody(settings.bucket, options);\n\n const createRes = await doFetch(createUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (createRes.status === 200 || createRes.status === 201) {\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n projectId,\n location: options.location,\n });\n return { bucketCreated: true };\n }\n\n if (createRes.status !== 409) {\n const text = await safeText(createRes);\n throw new Error(\n `setup: bucket create failed (${createRes.status}): ${text}`,\n );\n }\n\n logger.debug('setup: bucket already exists', { bucket: settings.bucket });\n await detectDrift(settings.bucket, token, options, logger, doFetch);\n return { bucketCreated: false };\n};\n\ninterface CreateBody {\n name: string;\n location?: string;\n storageClass?: string;\n versioning: { enabled: boolean };\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true };\n publicAccessPrevention: 'enforced';\n };\n labels?: Record<string, string>;\n encryption?: { defaultKmsKeyName: string };\n lifecycle?: { rule: unknown[] };\n}\n\nfunction buildCreateBody(bucket: string, options: Setup): CreateBody {\n const body: CreateBody = {\n name: bucket,\n location: options.location,\n storageClass: options.storageClass,\n versioning: { enabled: options.versioning ?? false },\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true },\n publicAccessPrevention: 'enforced',\n },\n };\n if (options.labels) body.labels = options.labels;\n if (options.kmsKeyName) {\n body.encryption = { defaultKmsKeyName: options.kmsKeyName };\n }\n if (options.lifecycle) body.lifecycle = options.lifecycle;\n return body;\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '';\n }\n}\n\ninterface BucketMetadata {\n location?: string;\n storageClass?: string;\n versioning?: { enabled?: boolean };\n iamConfiguration?: {\n uniformBucketLevelAccess?: { enabled?: boolean };\n publicAccessPrevention?: string;\n };\n labels?: Record<string, string>;\n}\n\nasync function detectDrift(\n bucket: string,\n token: string,\n options: Setup,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n let metadata: BucketMetadata;\n try {\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) {\n logger.debug('setup: drift check failed (non-fatal)', {\n status: res.status,\n });\n return;\n }\n const parsed: unknown = await res.json();\n metadata = readBucketMetadata(parsed);\n } catch (err) {\n logger.debug('setup: drift check failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n if (\n typeof metadata.location === 'string' &&\n options.location !== undefined &&\n metadata.location !== options.location\n ) {\n logger.warn('setup.drift', {\n field: 'location',\n declared: options.location,\n actual: metadata.location,\n });\n }\n\n if (\n typeof metadata.storageClass === 'string' &&\n options.storageClass !== undefined &&\n metadata.storageClass !== options.storageClass\n ) {\n logger.warn('setup.drift', {\n field: 'storageClass',\n declared: options.storageClass,\n actual: metadata.storageClass,\n });\n }\n\n const declaredVersioning = options.versioning ?? false;\n const actualVersioning = metadata.versioning?.enabled;\n if (\n typeof actualVersioning === 'boolean' &&\n actualVersioning !== declaredVersioning\n ) {\n logger.warn('setup.drift', {\n field: 'versioning',\n declared: declaredVersioning,\n actual: actualVersioning,\n });\n }\n\n const iam = metadata.iamConfiguration;\n if (iam) {\n const actualUniform = iam.uniformBucketLevelAccess?.enabled;\n if (typeof actualUniform === 'boolean' && actualUniform !== true) {\n logger.warn('setup.drift', {\n field: 'uniformBucketLevelAccess',\n declared: true,\n actual: actualUniform,\n });\n }\n const actualPap = iam.publicAccessPrevention;\n if (typeof actualPap === 'string' && actualPap !== 'enforced') {\n logger.warn('setup.drift', {\n field: 'publicAccessPrevention',\n declared: 'enforced',\n actual: actualPap,\n });\n }\n }\n\n if (options.labels) {\n const actualLabels = metadata.labels ?? {};\n if (!labelsEqual(options.labels, actualLabels)) {\n logger.warn('setup.drift', {\n field: 'labels',\n declared: options.labels,\n actual: actualLabels,\n });\n }\n }\n}\n\nfunction labelsEqual(\n a: Record<string, string>,\n b: Record<string, string>,\n): boolean {\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (a[key] !== b[key]) return false;\n }\n return true;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readBucketMetadata(value: unknown): BucketMetadata {\n if (!isRecord(value)) return {};\n const out: BucketMetadata = {};\n if (typeof value.location === 'string') out.location = value.location;\n if (typeof value.storageClass === 'string') {\n out.storageClass = value.storageClass;\n }\n if (isRecord(value.versioning)) {\n const enabled = value.versioning.enabled;\n if (typeof enabled === 'boolean') {\n out.versioning = { enabled };\n }\n }\n if (isRecord(value.iamConfiguration)) {\n const iam: BucketMetadata['iamConfiguration'] = {};\n const uniform = value.iamConfiguration.uniformBucketLevelAccess;\n if (isRecord(uniform) && typeof uniform.enabled === 'boolean') {\n iam.uniformBucketLevelAccess = { enabled: uniform.enabled };\n }\n const pap = value.iamConfiguration.publicAccessPrevention;\n if (typeof pap === 'string') iam.publicAccessPrevention = pap;\n out.iamConfiguration = iam;\n }\n if (isRecord(value.labels)) {\n const labels: Record<string, string> = {};\n for (const [k, v] of Object.entries(value.labels)) {\n if (typeof v === 'string') labels[k] = v;\n }\n out.labels = labels;\n }\n return out;\n}\n","import type { Store } from '@walkeros/core';\nimport type { ServiceAccountCredentials, Types } from './types';\n\n/**\n * Resolves the GCP project ID used to create a bucket in the GCS JSON API.\n *\n * Resolution order:\n * 1. `setup.projectId` (explicit override).\n * 2. the resolved credentials' `project_id` (when SA JSON is parsed and contains it).\n * 3. `process.env.GOOGLE_CLOUD_PROJECT` (Cloud Run / GKE convention).\n * 4. Throw with an actionable error message.\n *\n * `process.env.GOOGLE_CLOUD_PROJECT` is read at call time, not at module load,\n * so changes after import are honored.\n *\n * `credentials` is the already-resolved value (`config.credentials ??\n * settings.credentials`), not read off settings here, so `config.credentials`\n * is honored for project-id derivation.\n */\nexport function resolveProjectId(\n setup: { projectId?: string },\n credentials: Store.Credentials<Types> | undefined,\n): string {\n if (setup.projectId) return setup.projectId;\n\n const fromCreds = extractProjectIdFromCredentials(credentials);\n if (fromCreds) return fromCreds;\n\n const fromEnv = process.env.GOOGLE_CLOUD_PROJECT;\n if (fromEnv) return fromEnv;\n\n throw new Error(\n 'setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.',\n );\n}\n\n/**\n * Typed predicate: returns the `project_id` field from credentials when\n * present and a string. Avoids type casts by validating the runtime shape.\n *\n * - `string` credentials are parsed as JSON; non-JSON or non-object payloads\n * return undefined.\n * - object credentials are inspected directly. Real SA JSON contains\n * `project_id` even though `ServiceAccountCredentials` declares only the\n * fields the runtime needs (client_email, private_key).\n */\nfunction extractProjectIdFromCredentials(\n credentials: Store.Credentials<Types> | undefined,\n): string | undefined {\n if (!credentials) return undefined;\n\n if (typeof credentials === 'string') {\n let parsed: unknown;\n try {\n parsed = JSON.parse(credentials);\n } catch {\n return undefined;\n }\n return readStringField(parsed, 'project_id');\n }\n\n return readStringField(credentials, 'project_id');\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readStringField(\n value: ServiceAccountCredentials | unknown,\n field: string,\n): string | undefined {\n if (!isRecord(value)) return undefined;\n const fieldValue = value[field];\n return typeof fieldValue === 'string' ? fieldValue : undefined;\n}\n"],"mappings":"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","names":["setup","isRecord","isRecord","GCS_BASE","isRecord","isServiceAccountShape","parseCredentials","GCS_BASE"]}
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/auth.ts","../src/credentials.ts","../src/setup.ts","../src/setup-helpers.ts"],"sourcesContent":["import type { Logger, Store } from '@walkeros/core';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type {\n GcsStoreSettings,\n ServiceAccountCredentials,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { setup as gcsSetup } from './setup';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\nconst JSON_CONTENT_TYPE = 'application/json';\nconst DEFAULT_CONTENT_TYPE = 'application/octet-stream';\n\n// Minimal extension to content-type map for the file-mode asset-serving use\n// case (serving walker.js and friends back with a correct mime). Anything not\n// listed falls back to application/octet-stream.\nconst MIME_BY_EXT: Readonly<Record<string, string>> = {\n js: 'application/javascript',\n mjs: 'application/javascript',\n json: 'application/json',\n css: 'text/css',\n html: 'text/html',\n txt: 'text/plain',\n svg: 'image/svg+xml',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n ico: 'image/x-icon',\n wasm: 'application/wasm',\n map: 'application/json',\n};\n\nfunction mimeFromKey(key: string): string {\n const dot = key.lastIndexOf('.');\n if (dot < 0 || dot === key.length - 1) return DEFAULT_CONTENT_TYPE;\n const ext = key.slice(dot + 1).toLowerCase();\n return MIME_BY_EXT[ext] ?? DEFAULT_CONTENT_TYPE;\n}\n\n/**\n * Module-level cache for the bucket existence pre-check. Keyed by bucket\n * name (bucket names are globally unique in GCS). One HEAD per process per\n * bucket. The cache stores a Promise so concurrent first-operations share a\n * single in-flight request.\n *\n * Exported helpers below allow tests to reset/seed the cache.\n */\nconst bucketExistsCache: Map<string, Promise<boolean>> = new Map();\n\n/** @internal Test-only: clear the existence cache. */\nexport function __resetBucketExistenceCache(): void {\n bucketExistsCache.clear();\n}\n\n/** @internal Test-only: seed the existence cache for a bucket. */\nexport function __seedBucketExists(bucket: string): void {\n bucketExistsCache.set(bucket, Promise.resolve(true));\n}\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\n\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\n/**\n * Wrap bytes in a fresh Uint8Array backed by a plain ArrayBuffer so they\n * satisfy fetch's BodyInit contract. Avoids type casts: Node's Buffer\n * type is `Buffer<ArrayBufferLike>`, while the DOM's BodyInit (used by\n * fetch's RequestInit) expects `ArrayBufferView<ArrayBuffer>`. Accepts any\n * Uint8Array (Buffer included, plus the codec's plain Uint8Array output).\n */\nfunction bufferToBody(buf: Uint8Array): Uint8Array<ArrayBuffer> {\n const out = new ArrayBuffer(buf.byteLength);\n const view = new Uint8Array(out);\n view.set(buf);\n return view;\n}\n\nfunction assertGcsSettings(\n settings: Partial<GcsStoreSettings> | undefined,\n): asserts settings is GcsStoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeGcsInit: settings.bucket is required (non-empty string)',\n );\n }\n}\n\n/**\n * Resolve a project ID for the runtime hard-fail message. Uses the same\n * resolution order as setup but never throws: returns \"unknown\" when no\n * source is available so the error message is still actionable.\n */\nfunction resolveProjectIdForMessage(\n credentials: Store.Credentials<Types> | undefined,\n): string {\n try {\n return resolveProjectId({}, credentials);\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Verify the bucket exists. Issues `HEAD /storage/v1/b/<bucket>` once per\n * process per bucket. On 404, throws an actionable error pointing at the\n * setup command. On any other failure (non-2xx response or transport\n * error), treats the result as \"exists\" to avoid blocking legitimate\n * operations on a transient blip.\n */\nasync function ensureBucketExists(\n bucket: string,\n id: string,\n credentials: Store.Credentials<Types> | undefined,\n getToken: () => Promise<string>,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const existing = bucketExistsCache.get(bucket);\n if (existing !== undefined) {\n await existing;\n return;\n }\n\n const promise = (async (): Promise<boolean> => {\n let res: Response;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n res = await doFetch(url, {\n method: 'HEAD',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch (err) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n error: err instanceof Error ? err.message : String(err),\n });\n return true;\n }\n\n if (res.status === 404) {\n const projectId = resolveProjectIdForMessage(credentials);\n throw new Error(\n `GCS bucket not found: ${bucket} in project ${projectId}. Run \"walkeros setup store.${id}\" to create it.`,\n );\n }\n if (!res.ok) {\n logger.debug('ensureBucketExists check failed (non-fatal)', {\n bucket,\n status: res.status,\n });\n }\n return true;\n })();\n\n bucketExistsCache.set(bucket, promise);\n try {\n await promise;\n } catch (err) {\n // Drop the failing entry so the next call retries the check.\n bucketExistsCache.delete(bucket);\n throw err;\n }\n}\n\nexport const storeGcsInit: Store.Init<Types> = (context) => {\n assertGcsSettings(context.config.settings);\n const settings: GcsStoreSettings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\n const rawCreds = resolveCredentials(context.config, context.logger);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included; the global fetch is the runtime default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const bucketRaw = settings.bucket;\n const bucket = encodeURIComponent(bucketRaw);\n const id = context.id;\n const { logger } = context;\n // One mode per instance, decided at init. file:true persists raw bytes\n // byte-exact with a real mime; structured mode round-trips StoreValue\n // through the shared core codec, stored as application/json.\n const fileMode = context.config.file === true;\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n const config: Store.Config<Types> = {\n settings,\n env: context.config.env,\n id: context.config.id,\n logger: context.config.logger,\n // The declared byte mode; state and validation read it from here.\n file: context.config.file,\n // `setup` from the incoming context.config is typed as `unknown` because\n // the Init context narrows only the settings/env/initSettings slots and\n // not the setup-options slot. The flow runtime never uses this field at\n // runtime (setup is invoked separately by the CLI), so omit it from the\n // returned config without a cast.\n };\n\n return {\n type: 'gcs',\n config,\n setup: gcsSetup,\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return undefined;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n let bytes: Buffer;\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/download/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}?alt=media`;\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) return undefined;\n bytes = Buffer.from(await res.arrayBuffer());\n } catch {\n return undefined;\n }\n // File mode hands the raw bytes back untouched (Buffer is a Uint8Array,\n // a valid StoreValue leaf). Structured mode decodes the utf8-JSON payload.\n // A corrupt or empty payload (GCS can return ok:true with an empty body)\n // degrades to a miss rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n logger.debug('structured decode failed, degrading to miss', {\n key,\n error: err instanceof Error ? err.message : String(err),\n });\n return undefined;\n }\n },\n\n async set(key: string, value: Store.StoreValue): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n let body: Uint8Array<ArrayBuffer>;\n let contentType: string;\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (typeof value === 'string') {\n body = bufferToBody(Buffer.from(value));\n } else if (value instanceof Uint8Array) {\n body = bufferToBody(value);\n } else {\n throw new Error(\n 'storeGcsInit.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n contentType = mimeFromKey(key);\n } else {\n // Structured mode: serialize to utf8-JSON bytes, uploaded as JSON.\n body = bufferToBody(serializeStoreValue(value));\n contentType = JSON_CONTENT_TYPE;\n }\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n const token = await getToken();\n const url = `${GCS_BASE}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': contentType,\n },\n body,\n });\n },\n\n async delete(key: string): Promise<void> {\n const gcsKey = resolveKey(key);\n if (!gcsKey) return;\n\n await ensureBucketExists(\n bucketRaw,\n id,\n rawCreds,\n getToken,\n logger,\n doFetch,\n );\n\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}`;\n await doFetch(url, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch {\n /* GCS delete is idempotent */\n }\n },\n };\n};\n","import { createSign } from 'node:crypto';\nimport type { ServiceAccountCredentials } from './types';\n\nexport type TokenProvider = () => Promise<string>;\n\nconst METADATA_URL =\n 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token';\nconst OAUTH_URL = 'https://oauth2.googleapis.com/token';\nconst SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write';\nconst REFRESH_MARGIN_MS = 60_000;\n\ninterface CachedToken {\n token: string;\n expiresAt: number;\n}\n\nexport function createTokenProvider(\n credentials?: ServiceAccountCredentials,\n doFetch: typeof fetch = fetch,\n): TokenProvider {\n let cached: CachedToken | undefined;\n\n return async function getToken(): Promise<string> {\n if (cached && Date.now() < cached.expiresAt) return cached.token;\n\n cached = credentials\n ? await fetchServiceAccountToken(credentials, doFetch)\n : await fetchMetadataToken(doFetch);\n\n return cached.token;\n };\n}\n\nasync function fetchMetadataToken(doFetch: typeof fetch): Promise<CachedToken> {\n const res = await doFetch(METADATA_URL, {\n headers: { 'Metadata-Flavor': 'Google' },\n signal: AbortSignal.timeout(5000),\n });\n if (!res.ok) throw new Error(`Metadata server error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nasync function fetchServiceAccountToken(\n creds: ServiceAccountCredentials,\n doFetch: typeof fetch,\n): Promise<CachedToken> {\n const now = Math.floor(Date.now() / 1000);\n const jwt = signJwt(\n {\n iss: creds.client_email,\n scope: SCOPE,\n aud: OAUTH_URL,\n iat: now,\n exp: now + 3600,\n },\n creds.private_key,\n );\n\n const res = await doFetch(OAUTH_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`,\n });\n if (!res.ok) throw new Error(`Token exchange error: ${res.status}`);\n\n const data = (await res.json()) as {\n access_token: string;\n expires_in: number;\n };\n return {\n token: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - REFRESH_MARGIN_MS,\n };\n}\n\nfunction base64url(input: string | Buffer): string {\n const buf = typeof input === 'string' ? Buffer.from(input) : input;\n return buf.toString('base64url');\n}\n\nfunction signJwt(payload: object, privateKey: string): string {\n const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' }));\n const body = base64url(JSON.stringify(payload));\n const sign = createSign('RSA-SHA256');\n sign.update(`${header}.${body}`);\n return `${header}.${body}.${sign.sign(privateKey, 'base64url')}`;\n}\n","import type { Logger, Store } from '@walkeros/core';\nimport type { Types } from './types';\n\ntype ResolvedCredentials = Store.Credentials<Types>;\n\n/**\n * The two credential read sites narrow different slots: `store.ts` runs inside\n * the `Store.Init` context, where the typed `credentials` slot is widened back\n * to `unknown` (settings is `Partial`); `setup.ts` runs with the full\n * `Store.Config<Types>`, where `credentials` is `ResolvedCredentials`. Accept\n * `unknown` for both and narrow at runtime.\n */\ninterface CredentialSources {\n credentials?: unknown;\n settings?: { credentials?: unknown };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Narrow an unknown credential candidate to the resolved credential shape: a\n * JSON string (often a `$env` reference) or a parsed service-account object.\n * Anything else resolves to `undefined`; the downstream parser surfaces a\n * misconfiguration rather than this layer guessing.\n */\nfunction asCredential(value: unknown): ResolvedCredentials | undefined {\n if (typeof value === 'string') return value;\n if (\n isRecord(value) &&\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n ) {\n const { client_email, private_key, project_id } = value;\n return typeof project_id === 'string'\n ? { client_email, private_key, project_id }\n : { client_email, private_key };\n }\n return undefined;\n}\n\n/**\n * Resolve the credential source for the store, preferring the strictly-typed\n * `config.credentials` slot and falling back to the deprecated\n * `settings.credentials`. Returns the raw credential (string or object); the\n * caller parses it into a service account.\n *\n * Precedence is `??` (not `||`) so an explicitly-empty `config.credentials`\n * surfaces as a misconfiguration rather than silently falling through.\n *\n * Warn-once semantics: the warning fires from the init/setup read site, which\n * runs once per store instance. No module-level flag, so a second instance on\n * the deprecated path still warns.\n */\nexport function resolveCredentials(\n config: CredentialSources,\n logger?: Logger.Instance,\n): ResolvedCredentials | undefined {\n if (config.credentials !== undefined) return asCredential(config.credentials);\n\n const fromSettings = config.settings?.credentials;\n if (fromSettings !== undefined && logger) {\n logger.warn(\n 'gcs store: settings.credentials is deprecated, use config.credentials',\n );\n }\n return asCredential(fromSettings);\n}\n","import type { LifecycleContext, Logger, Store, SetupFn } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type {\n Env,\n GcsStoreSettings,\n ServiceAccountCredentials,\n Setup,\n Types,\n} from './types';\nimport { createTokenProvider } from './auth';\nimport { resolveCredentials } from './credentials';\nimport { resolveProjectId } from './setup-helpers';\n\nconst GCS_BASE = 'https://storage.googleapis.com';\n\n/**\n * Default setup options. Optional fields (lifecycle, kmsKeyName, labels,\n * projectId) are intentionally omitted so the resolved options carry\n * `undefined` for them and the body builder can skip writing those\n * properties to the create request payload.\n */\nexport const DEFAULT_SETUP: Setup = {\n location: 'EU',\n storageClass: 'STANDARD',\n versioning: false,\n};\n\nexport interface SetupResult {\n bucketCreated: boolean;\n}\n\n/**\n * Public alias kept for callers that imported the prior shape.\n * Equivalent to the framework's `Store.Config<Types>`.\n */\nexport type GcsStoreConfig = Store.Config<Types>;\n\n/**\n * Provision a GCS bucket described in the flow config.\n * Idempotent: 409 means the bucket exists, drift is logged but never patched.\n * Never auto-mutates an existing bucket.\n */\nexport const setup: SetupFn<GcsStoreConfig, Env> = async (\n context: LifecycleContext<GcsStoreConfig, Env>,\n) => {\n const { config, logger } = context;\n const options = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!options) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n\n const settings = config.settings;\n if (!settings || !settings.bucket) {\n throw new Error('setup: settings.bucket is required');\n }\n\n const rawCreds = resolveCredentials(config, logger);\n const projectId = resolveProjectId(options, rawCreds);\n const creds = parseCredentials(rawCreds);\n // An injected fetch (tests, simulate) carries every request, token exchange\n // included, as in the store runtime; the global fetch is the default.\n const doFetch = context.env?.fetch ?? context.config.env?.fetch ?? fetch;\n const getToken = createTokenProvider(creds, doFetch);\n const token = await getToken();\n\n const createUrl = `${GCS_BASE}/storage/v1/b?project=${encodeURIComponent(projectId)}`;\n const body = buildCreateBody(settings.bucket, options);\n\n const createRes = await doFetch(createUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (createRes.status === 200 || createRes.status === 201) {\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n projectId,\n location: options.location,\n });\n return { bucketCreated: true };\n }\n\n if (createRes.status !== 409) {\n const text = await safeText(createRes);\n throw new Error(\n `setup: bucket create failed (${createRes.status}): ${text}`,\n );\n }\n\n logger.debug('setup: bucket already exists', { bucket: settings.bucket });\n await detectDrift(settings.bucket, token, options, logger, doFetch);\n return { bucketCreated: false };\n};\n\ninterface CreateBody {\n name: string;\n location?: string;\n storageClass?: string;\n versioning: { enabled: boolean };\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true };\n publicAccessPrevention: 'enforced';\n };\n labels?: Record<string, string>;\n encryption?: { defaultKmsKeyName: string };\n lifecycle?: { rule: unknown[] };\n}\n\nfunction buildCreateBody(bucket: string, options: Setup): CreateBody {\n const body: CreateBody = {\n name: bucket,\n location: options.location,\n storageClass: options.storageClass,\n versioning: { enabled: options.versioning ?? false },\n iamConfiguration: {\n uniformBucketLevelAccess: { enabled: true },\n publicAccessPrevention: 'enforced',\n },\n };\n if (options.labels) body.labels = options.labels;\n if (options.kmsKeyName) {\n body.encryption = { defaultKmsKeyName: options.kmsKeyName };\n }\n if (options.lifecycle) body.lifecycle = options.lifecycle;\n return body;\n}\n\nfunction parseCredentials(\n credentials?: string | ServiceAccountCredentials,\n): ServiceAccountCredentials | undefined {\n if (!credentials) return undefined;\n if (typeof credentials === 'string') {\n const parsed: unknown = JSON.parse(credentials);\n if (isServiceAccountShape(parsed)) return parsed;\n return undefined;\n }\n return credentials;\n}\n\nfunction isServiceAccountShape(\n value: unknown,\n): value is ServiceAccountCredentials {\n if (!isRecord(value)) return false;\n return (\n typeof value.client_email === 'string' &&\n typeof value.private_key === 'string'\n );\n}\n\nasync function safeText(res: Response): Promise<string> {\n try {\n return await res.text();\n } catch {\n return '';\n }\n}\n\ninterface BucketMetadata {\n location?: string;\n storageClass?: string;\n versioning?: { enabled?: boolean };\n iamConfiguration?: {\n uniformBucketLevelAccess?: { enabled?: boolean };\n publicAccessPrevention?: string;\n };\n labels?: Record<string, string>;\n}\n\nasync function detectDrift(\n bucket: string,\n token: string,\n options: Setup,\n logger: Logger.Instance,\n doFetch: typeof fetch,\n): Promise<void> {\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n let metadata: BucketMetadata;\n try {\n const res = await doFetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) {\n logger.debug('setup: drift check failed (non-fatal)', {\n status: res.status,\n });\n return;\n }\n const parsed: unknown = await res.json();\n metadata = readBucketMetadata(parsed);\n } catch (err) {\n logger.debug('setup: drift check failed (non-fatal)', {\n error: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n if (\n typeof metadata.location === 'string' &&\n options.location !== undefined &&\n metadata.location !== options.location\n ) {\n logger.warn('setup.drift', {\n field: 'location',\n declared: options.location,\n actual: metadata.location,\n });\n }\n\n if (\n typeof metadata.storageClass === 'string' &&\n options.storageClass !== undefined &&\n metadata.storageClass !== options.storageClass\n ) {\n logger.warn('setup.drift', {\n field: 'storageClass',\n declared: options.storageClass,\n actual: metadata.storageClass,\n });\n }\n\n const declaredVersioning = options.versioning ?? false;\n const actualVersioning = metadata.versioning?.enabled;\n if (\n typeof actualVersioning === 'boolean' &&\n actualVersioning !== declaredVersioning\n ) {\n logger.warn('setup.drift', {\n field: 'versioning',\n declared: declaredVersioning,\n actual: actualVersioning,\n });\n }\n\n const iam = metadata.iamConfiguration;\n if (iam) {\n const actualUniform = iam.uniformBucketLevelAccess?.enabled;\n if (typeof actualUniform === 'boolean' && actualUniform !== true) {\n logger.warn('setup.drift', {\n field: 'uniformBucketLevelAccess',\n declared: true,\n actual: actualUniform,\n });\n }\n const actualPap = iam.publicAccessPrevention;\n if (typeof actualPap === 'string' && actualPap !== 'enforced') {\n logger.warn('setup.drift', {\n field: 'publicAccessPrevention',\n declared: 'enforced',\n actual: actualPap,\n });\n }\n }\n\n if (options.labels) {\n const actualLabels = metadata.labels ?? {};\n if (!labelsEqual(options.labels, actualLabels)) {\n logger.warn('setup.drift', {\n field: 'labels',\n declared: options.labels,\n actual: actualLabels,\n });\n }\n }\n}\n\nfunction labelsEqual(\n a: Record<string, string>,\n b: Record<string, string>,\n): boolean {\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (a[key] !== b[key]) return false;\n }\n return true;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readBucketMetadata(value: unknown): BucketMetadata {\n if (!isRecord(value)) return {};\n const out: BucketMetadata = {};\n if (typeof value.location === 'string') out.location = value.location;\n if (typeof value.storageClass === 'string') {\n out.storageClass = value.storageClass;\n }\n if (isRecord(value.versioning)) {\n const enabled = value.versioning.enabled;\n if (typeof enabled === 'boolean') {\n out.versioning = { enabled };\n }\n }\n if (isRecord(value.iamConfiguration)) {\n const iam: BucketMetadata['iamConfiguration'] = {};\n const uniform = value.iamConfiguration.uniformBucketLevelAccess;\n if (isRecord(uniform) && typeof uniform.enabled === 'boolean') {\n iam.uniformBucketLevelAccess = { enabled: uniform.enabled };\n }\n const pap = value.iamConfiguration.publicAccessPrevention;\n if (typeof pap === 'string') iam.publicAccessPrevention = pap;\n out.iamConfiguration = iam;\n }\n if (isRecord(value.labels)) {\n const labels: Record<string, string> = {};\n for (const [k, v] of Object.entries(value.labels)) {\n if (typeof v === 'string') labels[k] = v;\n }\n out.labels = labels;\n }\n return out;\n}\n","import type { Store } from '@walkeros/core';\nimport type { ServiceAccountCredentials, Types } from './types';\n\n/**\n * Resolves the GCP project ID used to create a bucket in the GCS JSON API.\n *\n * Resolution order:\n * 1. `setup.projectId` (explicit override).\n * 2. the resolved credentials' `project_id` (when SA JSON is parsed and contains it).\n * 3. `process.env.GOOGLE_CLOUD_PROJECT` (Cloud Run / GKE convention).\n * 4. Throw with an actionable error message.\n *\n * `process.env.GOOGLE_CLOUD_PROJECT` is read at call time, not at module load,\n * so changes after import are honored.\n *\n * `credentials` is the already-resolved value (`config.credentials ??\n * settings.credentials`), not read off settings here, so `config.credentials`\n * is honored for project-id derivation.\n */\nexport function resolveProjectId(\n setup: { projectId?: string },\n credentials: Store.Credentials<Types> | undefined,\n): string {\n if (setup.projectId) return setup.projectId;\n\n const fromCreds = extractProjectIdFromCredentials(credentials);\n if (fromCreds) return fromCreds;\n\n const fromEnv = process.env.GOOGLE_CLOUD_PROJECT;\n if (fromEnv) return fromEnv;\n\n throw new Error(\n 'setup: projectId is required. Set setup.projectId, provide a service account with project_id, or export GOOGLE_CLOUD_PROJECT.',\n );\n}\n\n/**\n * Typed predicate: returns the `project_id` field from credentials when\n * present and a string. Avoids type casts by validating the runtime shape.\n *\n * - `string` credentials are parsed as JSON; non-JSON or non-object payloads\n * return undefined.\n * - object credentials are inspected directly. Real SA JSON contains\n * `project_id` even though `ServiceAccountCredentials` declares only the\n * fields the runtime needs (client_email, private_key).\n */\nfunction extractProjectIdFromCredentials(\n credentials: Store.Credentials<Types> | undefined,\n): string | undefined {\n if (!credentials) return undefined;\n\n if (typeof credentials === 'string') {\n let parsed: unknown;\n try {\n parsed = JSON.parse(credentials);\n } catch {\n return undefined;\n }\n return readStringField(parsed, 'project_id');\n }\n\n return readStringField(credentials, 'project_id');\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readStringField(\n value: ServiceAccountCredentials | unknown,\n field: string,\n): string | undefined {\n if (!isRecord(value)) return undefined;\n const fieldValue = value[field];\n return typeof fieldValue === 'string' ? fieldValue : undefined;\n}\n"],"mappings":"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","names":["setup","isRecord","isRecord","GCS_BASE","isRecord","isServiceAccountShape","parseCredentials","GCS_BASE"]}
|
package/dist/walkerOS.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$meta": {
|
|
3
3
|
"package": "@walkeros/server-store-gcs",
|
|
4
|
-
"version": "4.7.0
|
|
4
|
+
"version": "4.7.0",
|
|
5
5
|
"type": "store",
|
|
6
6
|
"platform": [
|
|
7
7
|
"server"
|
|
8
8
|
],
|
|
9
|
+
"exports": {},
|
|
9
10
|
"docs": "https://www.walkeros.io/docs/stores/server/gcs",
|
|
10
11
|
"source": "https://github.com/elbwalker/walkerOS/tree/main/packages/server/stores/gcs/src"
|
|
11
12
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walkeros/server-store-gcs",
|
|
3
3
|
"description": "Google Cloud Storage for walkerOS server flows",
|
|
4
|
-
"version": "4.7.0
|
|
4
|
+
"version": "4.7.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"update": "npx npm-check-updates -u && npm update"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@walkeros/core": "4.7.0
|
|
36
|
+
"@walkeros/core": "4.7.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {},
|
|
39
39
|
"repository": {
|