@walkeros/server-store-gcs 4.7.0-next-1790245733116 → 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 +29 -13
- package/dist/dev.d.mts +25 -2
- package/dist/dev.d.ts +25 -2
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/dev.mjs +1 -1
- package/dist/dev.mjs.map +1 -1
- package/dist/index.d.mts +10 -3
- package/dist/index.d.ts +10 -3
- 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 +15 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
# @walkeros/server-store-gcs
|
|
2
2
|
|
|
3
|
-
## 4.7.0
|
|
3
|
+
## 4.7.0
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- 74821ed: Simulate records every vendor call through one recorder, `observeEnv`
|
|
8
|
+
from `@walkeros/core`, and runs offline: BigQuery, Data Manager and the
|
|
9
|
+
Sheets, GCS and S3 stores reach the network through `env`. Client and `Env`
|
|
10
|
+
types of BigQuery, Firehose, PostHog, SQS and Pub/Sub are structural.
|
|
11
|
+
`@walkeros/cli` exports `resolveExportName` and `selectDevExamples` and drops
|
|
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.
|
|
18
|
+
- Updated dependencies [74821ed]
|
|
8
19
|
- Updated dependencies [06b498a]
|
|
9
|
-
- Updated dependencies [
|
|
10
|
-
- Updated dependencies [
|
|
20
|
+
- Updated dependencies [74821ed]
|
|
21
|
+
- Updated dependencies [64b06de]
|
|
22
|
+
- Updated dependencies [4b4937f]
|
|
23
|
+
- Updated dependencies [74821ed]
|
|
24
|
+
- Updated dependencies [74821ed]
|
|
25
|
+
- Updated dependencies [74821ed]
|
|
11
26
|
- Updated dependencies [06b498a]
|
|
12
|
-
- Updated dependencies [
|
|
13
|
-
- Updated dependencies [
|
|
14
|
-
- Updated dependencies [
|
|
15
|
-
- Updated dependencies [
|
|
16
|
-
- Updated dependencies [
|
|
17
|
-
- Updated dependencies [
|
|
18
|
-
- Updated dependencies [
|
|
19
|
-
- Updated dependencies [
|
|
20
|
-
|
|
27
|
+
- Updated dependencies [74821ed]
|
|
28
|
+
- Updated dependencies [74821ed]
|
|
29
|
+
- Updated dependencies [74821ed]
|
|
30
|
+
- Updated dependencies [74821ed]
|
|
31
|
+
- Updated dependencies [74821ed]
|
|
32
|
+
- Updated dependencies [e860006]
|
|
33
|
+
- Updated dependencies [74821ed]
|
|
34
|
+
- Updated dependencies [74821ed]
|
|
35
|
+
- Updated dependencies [4f89234]
|
|
36
|
+
- @walkeros/core@4.7.0
|
|
21
37
|
|
|
22
38
|
## 4.6.1
|
|
23
39
|
|
package/dist/dev.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _walkeros_core_dev from '@walkeros/core/dev';
|
|
2
2
|
import { z } from '@walkeros/core/dev';
|
|
3
|
-
import {
|
|
3
|
+
import { Store, Flow, Hint } from '@walkeros/core';
|
|
4
4
|
|
|
5
5
|
declare const SettingsSchema: z.ZodObject<{
|
|
6
6
|
bucket: z.ZodString;
|
|
@@ -38,6 +38,28 @@ declare namespace index$1 {
|
|
|
38
38
|
export { type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, index$1_SetupSchema as SetupSchema, index$1_settings as settings, index$1_setup as setup };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Injected dependencies (tests, simulate). Every GCS and token request goes
|
|
43
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
44
|
+
*/
|
|
45
|
+
interface Env extends Store.BaseEnv {
|
|
46
|
+
fetch?: typeof fetch;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Create a fresh in-memory GCS backend. */
|
|
50
|
+
declare function createGcsFetch(): typeof fetch;
|
|
51
|
+
/** Each read of `fetch` is a fresh backend, so runs never share writes. */
|
|
52
|
+
declare const push: Env;
|
|
53
|
+
/** Every request goes through `fetch`; `args[0]` is the URL. */
|
|
54
|
+
declare const simulation: string[];
|
|
55
|
+
|
|
56
|
+
declare const env_createGcsFetch: typeof createGcsFetch;
|
|
57
|
+
declare const env_push: typeof push;
|
|
58
|
+
declare const env_simulation: typeof simulation;
|
|
59
|
+
declare namespace env {
|
|
60
|
+
export { env_createGcsFetch as createGcsFetch, env_push as push, env_simulation as simulation };
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
/** Read a file using Application Default Credentials (Cloud Run / GKE), file mode. */
|
|
42
64
|
declare const readWithAdc: Flow.StepExample;
|
|
43
65
|
/** Key is scoped under the configured prefix subdirectory. */
|
|
@@ -57,11 +79,12 @@ declare const serviceAccount: Store.Config;
|
|
|
57
79
|
declare const structuredKv: Store.Config;
|
|
58
80
|
|
|
59
81
|
declare const index_cloudRunAdc: typeof cloudRunAdc;
|
|
82
|
+
declare const index_env: typeof env;
|
|
60
83
|
declare const index_serviceAccount: typeof serviceAccount;
|
|
61
84
|
declare const index_step: typeof step;
|
|
62
85
|
declare const index_structuredKv: typeof structuredKv;
|
|
63
86
|
declare namespace index {
|
|
64
|
-
export { index_cloudRunAdc as cloudRunAdc, index_serviceAccount as serviceAccount, index_step as step, index_structuredKv as structuredKv };
|
|
87
|
+
export { index_cloudRunAdc as cloudRunAdc, index_env as env, index_serviceAccount as serviceAccount, index_step as step, index_structuredKv as structuredKv };
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
declare const hints: Hint.Hints;
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _walkeros_core_dev from '@walkeros/core/dev';
|
|
2
2
|
import { z } from '@walkeros/core/dev';
|
|
3
|
-
import {
|
|
3
|
+
import { Store, Flow, Hint } from '@walkeros/core';
|
|
4
4
|
|
|
5
5
|
declare const SettingsSchema: z.ZodObject<{
|
|
6
6
|
bucket: z.ZodString;
|
|
@@ -38,6 +38,28 @@ declare namespace index$1 {
|
|
|
38
38
|
export { type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, index$1_SetupSchema as SetupSchema, index$1_settings as settings, index$1_setup as setup };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Injected dependencies (tests, simulate). Every GCS and token request goes
|
|
43
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
44
|
+
*/
|
|
45
|
+
interface Env extends Store.BaseEnv {
|
|
46
|
+
fetch?: typeof fetch;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Create a fresh in-memory GCS backend. */
|
|
50
|
+
declare function createGcsFetch(): typeof fetch;
|
|
51
|
+
/** Each read of `fetch` is a fresh backend, so runs never share writes. */
|
|
52
|
+
declare const push: Env;
|
|
53
|
+
/** Every request goes through `fetch`; `args[0]` is the URL. */
|
|
54
|
+
declare const simulation: string[];
|
|
55
|
+
|
|
56
|
+
declare const env_createGcsFetch: typeof createGcsFetch;
|
|
57
|
+
declare const env_push: typeof push;
|
|
58
|
+
declare const env_simulation: typeof simulation;
|
|
59
|
+
declare namespace env {
|
|
60
|
+
export { env_createGcsFetch as createGcsFetch, env_push as push, env_simulation as simulation };
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
/** Read a file using Application Default Credentials (Cloud Run / GKE), file mode. */
|
|
42
64
|
declare const readWithAdc: Flow.StepExample;
|
|
43
65
|
/** Key is scoped under the configured prefix subdirectory. */
|
|
@@ -57,11 +79,12 @@ declare const serviceAccount: Store.Config;
|
|
|
57
79
|
declare const structuredKv: Store.Config;
|
|
58
80
|
|
|
59
81
|
declare const index_cloudRunAdc: typeof cloudRunAdc;
|
|
82
|
+
declare const index_env: typeof env;
|
|
60
83
|
declare const index_serviceAccount: typeof serviceAccount;
|
|
61
84
|
declare const index_step: typeof step;
|
|
62
85
|
declare const index_structuredKv: typeof structuredKv;
|
|
63
86
|
declare namespace index {
|
|
64
|
-
export { index_cloudRunAdc as cloudRunAdc, index_serviceAccount as serviceAccount, index_step as step, index_structuredKv as structuredKv };
|
|
87
|
+
export { index_cloudRunAdc as cloudRunAdc, index_env as env, index_serviceAccount as serviceAccount, index_step as step, index_structuredKv as structuredKv };
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
declare const hints: Hint.Hints;
|
package/dist/dev.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,
|
|
1
|
+
"use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyNames,s=Object.prototype.hasOwnProperty,n=(e,r)=>{for(var o in r)t(e,o,{get:r[o],enumerable:!0})},i={};n(i,{examples:()=>b,hints:()=>R,schemas:()=>a}),module.exports=(e=i,((e,n,i,a)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let c of o(n))s.call(e,c)||c===i||t(e,c,{get:()=>n[c],enumerable:!(a=r(n,c))||a.enumerable});return e})(t({},"__esModule",{value:!0}),e));var a={};n(a,{SettingsSchema:()=>u,SetupSchema:()=>p,settings:()=>g,setup:()=>f});var c=require("@walkeros/core/dev"),l=require("@walkeros/core/dev"),u=l.z.object({bucket:l.z.string().min(1).describe("GCS bucket name"),prefix:l.z.string().optional().describe("Key prefix prepended to all store keys for scoping"),credentials:l.z.union([l.z.string(),l.z.object({client_email:l.z.string().describe("Service account email"),private_key:l.z.string().describe("RSA private key in PEM format")})]).optional().describe("Service account JSON (string or object). Omit for ADC on Cloud Run/GKE (deprecated: use config.credentials)")}),d=require("@walkeros/core/dev"),p=d.z.object({projectId:d.z.string().optional().describe("GCP project that owns the bucket. Falls back to credentials.project_id then GOOGLE_CLOUD_PROJECT env"),location:d.z.string().default("EU").describe("Geographic location (multi-region or region code)"),storageClass:d.z.enum(["STANDARD","NEARLINE","COLDLINE","ARCHIVE"]).default("STANDARD").describe("Default storage class for objects"),versioning:d.z.boolean().default(!1).describe("Enable object versioning at create time"),labels:d.z.record(d.z.string(),d.z.string()).optional().describe("Labels for cost allocation"),kmsKeyName:d.z.string().optional().describe("Customer-managed encryption key for at-rest encryption")}).describe('Provisioning options for "walkeros setup store.<id>". Idempotent: never mutates an existing bucket.'),g=(0,c.zodToSchema)(u),f=(0,c.zodToSchema)(p),b={};n(b,{cloudRunAdc:()=>z,env:()=>m,serviceAccount:()=>C,step:()=>j,structuredKv:()=>A});var m={};n(m,{createGcsFetch:()=>v,push:()=>k,simulation:()=>w});var h={access_token:"mock-access-token",expires_in:3600};function y(e,t=200){return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":"application/json"}})}function v(){const e=new Map;return async(t,r)=>{const o=new URL(function(e){return"string"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),s=r?.method??"GET";if("https://oauth2.googleapis.com"===o.origin)return y(h);if("metadata.google.internal"===o.hostname)return y(h);if(o.pathname.startsWith("/upload/")&&"POST"===s){const t=o.searchParams.get("name")??"";return e.set(t,(n=r?.body,"string"==typeof n?(new TextEncoder).encode(n):n instanceof Uint8Array?new Uint8Array(n):new Uint8Array)),y({name:t})}var n;const i=/^\/download\/storage\/v1\/b\/[^/]+\/o\/(.+)$/.exec(o.pathname);if(i){const t=e.get(decodeURIComponent(i[1]));return t?new Response(t,{status:200}):y({error:{code:404}},404)}const a=/^\/storage\/v1\/b\/[^/]+\/o\/(.+)$/.exec(o.pathname);return a&&"DELETE"===s?(e.delete(decodeURIComponent(a[1])),new Response(null,{status:204})):/^\/storage\/v1\/b\/[^/]+$/.test(o.pathname)?new Response(null,{status:200}):y({error:{code:400}},400)}}var k={get fetch(){return v()}},w=["fetch"],j={};n(j,{prefixScoping:()=>x,readWithAdc:()=>S});var S={title:"Read with ADC",description:"Read object from GCS bucket using ADC (no credentials needed), bytes byte-exact",in:{operation:"get",key:"walker.js"},out:[["get","walker.js","Bytes<(function(){...})()>"]]},x={title:"Prefix scoping",description:'Key "walker.js" with prefix "public/" resolves to GCS path "public/walker.js"',in:{operation:"get",key:"walker.js",settings:{bucket:"my-assets",prefix:"public"}},out:[["get","public/walker.js","Bytes<...>"]]},z={settings:{bucket:"my-assets",prefix:"public"},file:!0},C={settings:{bucket:"my-assets",prefix:"public"},credentials:"$env.GCS_SA_KEY",file:!0},A={settings:{bucket:"my-state"}},R={"file-mode":{text:"Default mode is structured: values are StoreValue data round-tripped through the shared core codec and stored with Content-Type application/json, good for session state, lookups, and cached responses. Set config.file: true to persist raw bytes byte-exact with the real mime derived from the key (set accepts Uint8Array or string), the mode for serving assets such as walker.js via the file transformer. One store instance is exactly one mode. Do not pair file: true with a cache; the cache wraps structured stores and flow_validate flags the combination."}};//# sourceMappingURL=dev.js.map
|
package/dist/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/setup.ts","../src/examples/index.ts","../src/examples/step.ts","../src/hints.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\nexport { hints } from './hints';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { SetupSchema } from './setup';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { SetupSchema } from './setup';\n\nexport const settings = zodToSchema(SettingsSchema);\nexport const setup = zodToSchema(SetupSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n bucket: z.string().min(1).describe('GCS bucket name'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n credentials: z\n .union([\n z.string(),\n z.object({\n client_email: z.string().describe('Service account email'),\n private_key: z.string().describe('RSA private key in PEM format'),\n }),\n ])\n .optional()\n .describe(\n 'Service account JSON (string or object). Omit for ADC on Cloud Run/GKE (deprecated: use config.credentials)',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n projectId: z\n .string()\n .optional()\n .describe(\n 'GCP project that owns the bucket. Falls back to credentials.project_id then GOOGLE_CLOUD_PROJECT env',\n ),\n location: z\n .string()\n .default('EU')\n .describe('Geographic location (multi-region or region code)'),\n storageClass: z\n .enum(['STANDARD', 'NEARLINE', 'COLDLINE', 'ARCHIVE'])\n .default('STANDARD')\n .describe('Default storage class for objects'),\n versioning: z\n .boolean()\n .default(false)\n .describe('Enable object versioning at create time'),\n labels: z\n .record(z.string(), z.string())\n .optional()\n .describe('Labels for cost allocation'),\n kmsKeyName: z\n .string()\n .optional()\n .describe('Customer-managed encryption key for at-rest encryption'),\n // lifecycle rules intentionally omitted from this schema: the type is\n // deeply nested and not useful to autocomplete in MCP discovery for v1.\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: never mutates an existing bucket.',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\n/** Cloud Run with ADC, serving assets byte-exact (file mode) */\nexport const cloudRunAdc: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n file: true,\n};\n\n/** Explicit service account for non-GCP environments (file mode) */\nexport const serviceAccount: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n credentials: '$env.GCS_SA_KEY',\n file: true,\n};\n\n/** GCS as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n },\n};\n\nexport * as step from './step';\n","import type { Flow } from '@walkeros/core';\n\n/** Read a file using Application Default Credentials (Cloud Run / GKE), file mode. */\nexport const readWithAdc: Flow.StepExample = {\n title: 'Read with ADC',\n description:\n 'Read object from GCS bucket using ADC (no credentials needed), bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped under the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to GCS path \"public/walker.js\"',\n in: {\n operation: 'get',\n key: 'walker.js',\n settings: { bucket: 'my-assets', prefix: 'public' },\n },\n out: [['get', 'public/walker.js', 'Bytes<...>']],\n};\n","import type { Hint } from '@walkeros/core';\n\nexport const hints: Hint.Hints = {\n 'file-mode': {\n text: 'Default mode is structured: values are StoreValue data round-tripped through the shared core codec and stored with Content-Type application/json, good for session state, lookups, and cached responses. Set config.file: true to persist raw bytes byte-exact with the real mime derived from the key (set accepts Uint8Array or string), the mode for serving assets such as walker.js via the file transformer. One store instance is exactly one mode. Do not pair file: true with a cache; the cache wraps structured stores and flow_validate flags the combination.',\n },\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,cAA4B;;;ACA5B,iBAAkB;AAEX,IAAM,iBAAiB,aAAE,OAAO;AAAA,EACrC,QAAQ,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,iBAAiB;AAAA,EACpD,QAAQ,aACL,OAAO,EACP,SAAS,EACT,SAAS,oDAAoD;AAAA,EAChE,aAAa,aACV,MAAM;AAAA,IACL,aAAE,OAAO;AAAA,IACT,aAAE,OAAO;AAAA,MACP,cAAc,aAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,MACzD,aAAa,aAAE,OAAO,EAAE,SAAS,+BAA+B;AAAA,IAClE,CAAC;AAAA,EACH,CAAC,EACA,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;;;ACpBD,IAAAC,cAAkB;AAEX,IAAM,cAAc,cACxB,OAAO;AAAA,EACN,WAAW,cACR,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,UAAU,cACP,OAAO,EACP,QAAQ,IAAI,EACZ,SAAS,mDAAmD;AAAA,EAC/D,cAAc,cACX,KAAK,CAAC,YAAY,YAAY,YAAY,SAAS,CAAC,EACpD,QAAQ,UAAU,EAClB,SAAS,mCAAmC;AAAA,EAC/C,YAAY,cACT,QAAQ,EACR,QAAQ,KAAK,EACb,SAAS,yCAAyC;AAAA,EACrD,QAAQ,cACL,OAAO,cAAE,OAAO,GAAG,cAAE,OAAO,CAAC,EAC7B,SAAS,EACT,SAAS,4BAA4B;AAAA,EACxC,YAAY,cACT,OAAO,EACP,SAAS,EACT,SAAS,wDAAwD;AAAA;AAAA;AAGtE,CAAC,EACA;AAAA,EACC;AACF;;;AF5BK,IAAM,eAAW,yBAAY,cAAc;AAC3C,IAAM,YAAQ,yBAAY,WAAW;;;AGR5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,cAAgC;AAAA,EAC3C,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,EAAE,WAAW,OAAO,KAAK,YAAY;AAAA,EACzC,KAAK,CAAC,CAAC,OAAO,aAAa,4BAA4B,CAAC;AAC1D;AAGO,IAAM,gBAAkC;AAAA,EAC7C,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI;AAAA,IACF,WAAW;AAAA,IACX,KAAK;AAAA,IACL,UAAU,EAAE,QAAQ,aAAa,QAAQ,SAAS;AAAA,EACpD;AAAA,EACA,KAAK,CAAC,CAAC,OAAO,oBAAoB,YAAY,CAAC;AACjD;;;ADnBO,IAAM,cAA4B;AAAA,EACvC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,MAAM;AACR;AAGO,IAAM,iBAA+B;AAAA,EAC1C,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,aAAa;AAAA,EACb,MAAM;AACR;AAGO,IAAM,eAA6B;AAAA,EACxC,UAAU;AAAA,IACR,QAAQ;AAAA,EACV;AACF;;;AExBO,IAAM,QAAoB;AAAA,EAC/B,aAAa;AAAA,IACX,MAAM;AAAA,EACR;AACF;","names":["import_dev","import_dev"]}
|
|
1
|
+
{"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/setup.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts","../src/hints.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\nexport { hints } from './hints';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { SetupSchema } from './setup';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { SetupSchema } from './setup';\n\nexport const settings = zodToSchema(SettingsSchema);\nexport const setup = zodToSchema(SetupSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n bucket: z.string().min(1).describe('GCS bucket name'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n credentials: z\n .union([\n z.string(),\n z.object({\n client_email: z.string().describe('Service account email'),\n private_key: z.string().describe('RSA private key in PEM format'),\n }),\n ])\n .optional()\n .describe(\n 'Service account JSON (string or object). Omit for ADC on Cloud Run/GKE (deprecated: use config.credentials)',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n projectId: z\n .string()\n .optional()\n .describe(\n 'GCP project that owns the bucket. Falls back to credentials.project_id then GOOGLE_CLOUD_PROJECT env',\n ),\n location: z\n .string()\n .default('EU')\n .describe('Geographic location (multi-region or region code)'),\n storageClass: z\n .enum(['STANDARD', 'NEARLINE', 'COLDLINE', 'ARCHIVE'])\n .default('STANDARD')\n .describe('Default storage class for objects'),\n versioning: z\n .boolean()\n .default(false)\n .describe('Enable object versioning at create time'),\n labels: z\n .record(z.string(), z.string())\n .optional()\n .describe('Labels for cost allocation'),\n kmsKeyName: z\n .string()\n .optional()\n .describe('Customer-managed encryption key for at-rest encryption'),\n // lifecycle rules intentionally omitted from this schema: the type is\n // deeply nested and not useful to autocomplete in MCP discovery for v1.\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: never mutates an existing bucket.',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\nexport * as env from './env';\n\n/** Cloud Run with ADC, serving assets byte-exact (file mode) */\nexport const cloudRunAdc: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n file: true,\n};\n\n/** Explicit service account for non-GCP environments (file mode) */\nexport const serviceAccount: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n credentials: '$env.GCS_SA_KEY',\n file: true,\n};\n\n/** GCS as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n },\n};\n\nexport * as step from './step';\n","import type { Env } from '../types';\n\n/**\n * Mock env for the GCS store: an in-memory bucket behind `fetch`.\n *\n * It answers every request the store makes: the token exchange (service\n * account) or metadata token (ADC), the bucket existence check, and object\n * download, upload and delete. Objects live in a `Map` keyed by object name,\n * so a `get` after a `set` returns what was written. Any bucket exists.\n */\n\nconst TOKEN_BODY = { access_token: 'mock-access-token', expires_in: 3600 };\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nfunction requestUrl(input: Parameters<typeof fetch>[0]): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction bodyBytes(body: RequestInit['body']): Uint8Array<ArrayBuffer> {\n if (typeof body === 'string') return new TextEncoder().encode(body);\n if (body instanceof Uint8Array) return new Uint8Array(body);\n return new Uint8Array();\n}\n\n/** Create a fresh in-memory GCS backend. */\nexport function createGcsFetch(): typeof fetch {\n const objects = new Map<string, Uint8Array<ArrayBuffer>>();\n\n return async (input, init) => {\n const url = new URL(requestUrl(input));\n const method = init?.method ?? 'GET';\n\n if (url.origin === 'https://oauth2.googleapis.com')\n return jsonResponse(TOKEN_BODY);\n if (url.hostname === 'metadata.google.internal')\n return jsonResponse(TOKEN_BODY);\n\n // Upload: POST /upload/storage/v1/b/<bucket>/o?uploadType=media&name=<key>\n if (url.pathname.startsWith('/upload/') && method === 'POST') {\n const name = url.searchParams.get('name') ?? '';\n objects.set(name, bodyBytes(init?.body));\n return jsonResponse({ name });\n }\n\n // Download: GET /download/storage/v1/b/<bucket>/o/<key>?alt=media\n const download = /^\\/download\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(\n url.pathname,\n );\n if (download) {\n const bytes = objects.get(decodeURIComponent(download[1]));\n if (!bytes) return jsonResponse({ error: { code: 404 } }, 404);\n return new Response(bytes, { status: 200 });\n }\n\n // Delete: DELETE /storage/v1/b/<bucket>/o/<key>\n const object = /^\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(url.pathname);\n if (object && method === 'DELETE') {\n objects.delete(decodeURIComponent(object[1]));\n return new Response(null, { status: 204 });\n }\n\n // Bucket existence: HEAD /storage/v1/b/<bucket>\n if (/^\\/storage\\/v1\\/b\\/[^/]+$/.test(url.pathname))\n return new Response(null, { status: 200 });\n\n return jsonResponse({ error: { code: 400 } }, 400);\n };\n}\n\n/** Each read of `fetch` is a fresh backend, so runs never share writes. */\nexport const push: Env = {\n get fetch() {\n return createGcsFetch();\n },\n};\n\n/** Every request goes through `fetch`; `args[0]` is the URL. */\nexport const simulation = ['fetch'];\n","import type { Flow } from '@walkeros/core';\n\n/** Read a file using Application Default Credentials (Cloud Run / GKE), file mode. */\nexport const readWithAdc: Flow.StepExample = {\n title: 'Read with ADC',\n description:\n 'Read object from GCS bucket using ADC (no credentials needed), bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped under the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to GCS path \"public/walker.js\"',\n in: {\n operation: 'get',\n key: 'walker.js',\n settings: { bucket: 'my-assets', prefix: 'public' },\n },\n out: [['get', 'public/walker.js', 'Bytes<...>']],\n};\n","import type { Hint } from '@walkeros/core';\n\nexport const hints: Hint.Hints = {\n 'file-mode': {\n text: 'Default mode is structured: values are StoreValue data round-tripped through the shared core codec and stored with Content-Type application/json, good for session state, lookups, and cached responses. Set config.file: true to persist raw bytes byte-exact with the real mime derived from the key (set accepts Uint8Array or string), the mode for serving assets such as walker.js via the file transformer. One store instance is exactly one mode. Do not pair file: true with a cache; the cache wraps structured stores and flow_validate flags the combination.',\n },\n};\n"],"mappings":"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","names":["import_dev","import_dev"]}
|
package/dist/dev.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=Object.defineProperty,t=(t,r)=>{for(var s in r)e(t,s,{get:r[s],enumerable:!0})},r={};t(r,{SettingsSchema:()=>
|
|
1
|
+
var e=Object.defineProperty,t=(t,r)=>{for(var s in r)e(t,s,{get:r[s],enumerable:!0})},r={};t(r,{SettingsSchema:()=>n,SetupSchema:()=>a,settings:()=>c,setup:()=>l});import{zodToSchema as s}from"@walkeros/core/dev";import{z as o}from"@walkeros/core/dev";var n=o.object({bucket:o.string().min(1).describe("GCS bucket name"),prefix:o.string().optional().describe("Key prefix prepended to all store keys for scoping"),credentials:o.union([o.string(),o.object({client_email:o.string().describe("Service account email"),private_key:o.string().describe("RSA private key in PEM format")})]).optional().describe("Service account JSON (string or object). Omit for ADC on Cloud Run/GKE (deprecated: use config.credentials)")});import{z as i}from"@walkeros/core/dev";var a=i.object({projectId:i.string().optional().describe("GCP project that owns the bucket. Falls back to credentials.project_id then GOOGLE_CLOUD_PROJECT env"),location:i.string().default("EU").describe("Geographic location (multi-region or region code)"),storageClass:i.enum(["STANDARD","NEARLINE","COLDLINE","ARCHIVE"]).default("STANDARD").describe("Default storage class for objects"),versioning:i.boolean().default(!1).describe("Enable object versioning at create time"),labels:i.record(i.string(),i.string()).optional().describe("Labels for cost allocation"),kmsKeyName:i.string().optional().describe("Customer-managed encryption key for at-rest encryption")}).describe('Provisioning options for "walkeros setup store.<id>". Idempotent: never mutates an existing bucket.'),c=s(n),l=s(a),d={};t(d,{cloudRunAdc:()=>k,env:()=>p,serviceAccount:()=>w,step:()=>h,structuredKv:()=>S});var p={};t(p,{createGcsFetch:()=>f,push:()=>m,simulation:()=>b});var u={access_token:"mock-access-token",expires_in:3600};function g(e,t=200){return new Response(JSON.stringify(e),{status:t,headers:{"Content-Type":"application/json"}})}function f(){const e=new Map;return async(t,r)=>{const s=new URL(function(e){return"string"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),o=r?.method??"GET";if("https://oauth2.googleapis.com"===s.origin)return g(u);if("metadata.google.internal"===s.hostname)return g(u);if(s.pathname.startsWith("/upload/")&&"POST"===o){const t=s.searchParams.get("name")??"";return e.set(t,(n=r?.body,"string"==typeof n?(new TextEncoder).encode(n):n instanceof Uint8Array?new Uint8Array(n):new Uint8Array)),g({name:t})}var n;const i=/^\/download\/storage\/v1\/b\/[^/]+\/o\/(.+)$/.exec(s.pathname);if(i){const t=e.get(decodeURIComponent(i[1]));return t?new Response(t,{status:200}):g({error:{code:404}},404)}const a=/^\/storage\/v1\/b\/[^/]+\/o\/(.+)$/.exec(s.pathname);return a&&"DELETE"===o?(e.delete(decodeURIComponent(a[1])),new Response(null,{status:204})):/^\/storage\/v1\/b\/[^/]+$/.test(s.pathname)?new Response(null,{status:200}):g({error:{code:400}},400)}}var m={get fetch(){return f()}},b=["fetch"],h={};t(h,{prefixScoping:()=>v,readWithAdc:()=>y});var y={title:"Read with ADC",description:"Read object from GCS bucket using ADC (no credentials needed), bytes byte-exact",in:{operation:"get",key:"walker.js"},out:[["get","walker.js","Bytes<(function(){...})()>"]]},v={title:"Prefix scoping",description:'Key "walker.js" with prefix "public/" resolves to GCS path "public/walker.js"',in:{operation:"get",key:"walker.js",settings:{bucket:"my-assets",prefix:"public"}},out:[["get","public/walker.js","Bytes<...>"]]},k={settings:{bucket:"my-assets",prefix:"public"},file:!0},w={settings:{bucket:"my-assets",prefix:"public"},credentials:"$env.GCS_SA_KEY",file:!0},S={settings:{bucket:"my-state"}},j={"file-mode":{text:"Default mode is structured: values are StoreValue data round-tripped through the shared core codec and stored with Content-Type application/json, good for session state, lookups, and cached responses. Set config.file: true to persist raw bytes byte-exact with the real mime derived from the key (set accepts Uint8Array or string), the mode for serving assets such as walker.js via the file transformer. One store instance is exactly one mode. Do not pair file: true with a cache; the cache wraps structured stores and flow_validate flags the combination."}};export{d as examples,j as hints,r as schemas};//# sourceMappingURL=dev.mjs.map
|
package/dist/dev.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/setup.ts","../src/examples/index.ts","../src/examples/step.ts","../src/hints.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { SetupSchema } from './setup';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { SetupSchema } from './setup';\n\nexport const settings = zodToSchema(SettingsSchema);\nexport const setup = zodToSchema(SetupSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n bucket: z.string().min(1).describe('GCS bucket name'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n credentials: z\n .union([\n z.string(),\n z.object({\n client_email: z.string().describe('Service account email'),\n private_key: z.string().describe('RSA private key in PEM format'),\n }),\n ])\n .optional()\n .describe(\n 'Service account JSON (string or object). Omit for ADC on Cloud Run/GKE (deprecated: use config.credentials)',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n projectId: z\n .string()\n .optional()\n .describe(\n 'GCP project that owns the bucket. Falls back to credentials.project_id then GOOGLE_CLOUD_PROJECT env',\n ),\n location: z\n .string()\n .default('EU')\n .describe('Geographic location (multi-region or region code)'),\n storageClass: z\n .enum(['STANDARD', 'NEARLINE', 'COLDLINE', 'ARCHIVE'])\n .default('STANDARD')\n .describe('Default storage class for objects'),\n versioning: z\n .boolean()\n .default(false)\n .describe('Enable object versioning at create time'),\n labels: z\n .record(z.string(), z.string())\n .optional()\n .describe('Labels for cost allocation'),\n kmsKeyName: z\n .string()\n .optional()\n .describe('Customer-managed encryption key for at-rest encryption'),\n // lifecycle rules intentionally omitted from this schema: the type is\n // deeply nested and not useful to autocomplete in MCP discovery for v1.\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: never mutates an existing bucket.',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\n/** Cloud Run with ADC, serving assets byte-exact (file mode) */\nexport const cloudRunAdc: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n file: true,\n};\n\n/** Explicit service account for non-GCP environments (file mode) */\nexport const serviceAccount: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n credentials: '$env.GCS_SA_KEY',\n file: true,\n};\n\n/** GCS as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n },\n};\n\nexport * as step from './step';\n","import type { Flow } from '@walkeros/core';\n\n/** Read a file using Application Default Credentials (Cloud Run / GKE), file mode. */\nexport const readWithAdc: Flow.StepExample = {\n title: 'Read with ADC',\n description:\n 'Read object from GCS bucket using ADC (no credentials needed), bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped under the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to GCS path \"public/walker.js\"',\n in: {\n operation: 'get',\n key: 'walker.js',\n settings: { bucket: 'my-assets', prefix: 'public' },\n },\n out: [['get', 'public/walker.js', 'Bytes<...>']],\n};\n","import type { Hint } from '@walkeros/core';\n\nexport const hints: Hint.Hints = {\n 'file-mode': {\n text: 'Default mode is structured: values are StoreValue data round-tripped through the shared core codec and stored with Content-Type application/json, good for session state, lookups, and cached responses. Set config.file: true to persist raw bytes byte-exact with the real mime derived from the key (set accepts Uint8Array or string), the mode for serving assets such as walker.js via the file transformer. One store instance is exactly one mode. Do not pair file: true with a cache; the cache wraps structured stores and flow_validate flags the combination.',\n },\n};\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,SAAS;AAEX,IAAM,iBAAiB,EAAE,OAAO;AAAA,EACrC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,iBAAiB;AAAA,EACpD,QAAQ,EACL,OAAO,EACP,SAAS,EACT,SAAS,oDAAoD;AAAA,EAChE,aAAa,EACV,MAAM;AAAA,IACL,EAAE,OAAO;AAAA,IACT,EAAE,OAAO;AAAA,MACP,cAAc,EAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,MACzD,aAAa,EAAE,OAAO,EAAE,SAAS,+BAA+B;AAAA,IAClE,CAAC;AAAA,EACH,CAAC,EACA,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;;;ACpBD,SAAS,KAAAA,UAAS;AAEX,IAAM,cAAcA,GACxB,OAAO;AAAA,EACN,WAAWA,GACR,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,UAAUA,GACP,OAAO,EACP,QAAQ,IAAI,EACZ,SAAS,mDAAmD;AAAA,EAC/D,cAAcA,GACX,KAAK,CAAC,YAAY,YAAY,YAAY,SAAS,CAAC,EACpD,QAAQ,UAAU,EAClB,SAAS,mCAAmC;AAAA,EAC/C,YAAYA,GACT,QAAQ,EACR,QAAQ,KAAK,EACb,SAAS,yCAAyC;AAAA,EACrD,QAAQA,GACL,OAAOA,GAAE,OAAO,GAAGA,GAAE,OAAO,CAAC,EAC7B,SAAS,EACT,SAAS,4BAA4B;AAAA,EACxC,YAAYA,GACT,OAAO,EACP,SAAS,EACT,SAAS,wDAAwD;AAAA;AAAA;AAGtE,CAAC,EACA;AAAA,EACC;AACF;;;AF5BK,IAAM,WAAW,YAAY,cAAc;AAC3C,IAAM,QAAQ,YAAY,WAAW;;;AGR5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,cAAgC;AAAA,EAC3C,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI,EAAE,WAAW,OAAO,KAAK,YAAY;AAAA,EACzC,KAAK,CAAC,CAAC,OAAO,aAAa,4BAA4B,CAAC;AAC1D;AAGO,IAAM,gBAAkC;AAAA,EAC7C,OAAO;AAAA,EACP,aACE;AAAA,EACF,IAAI;AAAA,IACF,WAAW;AAAA,IACX,KAAK;AAAA,IACL,UAAU,EAAE,QAAQ,aAAa,QAAQ,SAAS;AAAA,EACpD;AAAA,EACA,KAAK,CAAC,CAAC,OAAO,oBAAoB,YAAY,CAAC;AACjD;;;ADnBO,IAAM,cAA4B;AAAA,EACvC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,MAAM;AACR;AAGO,IAAM,iBAA+B;AAAA,EAC1C,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,aAAa;AAAA,EACb,MAAM;AACR;AAGO,IAAM,eAA6B;AAAA,EACxC,UAAU;AAAA,IACR,QAAQ;AAAA,EACV;AACF;;;AExBO,IAAM,QAAoB;AAAA,EAC/B,aAAa;AAAA,IACX,MAAM;AAAA,EACR;AACF;","names":["z"]}
|
|
1
|
+
{"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/setup.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts","../src/hints.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { SetupSchema } from './setup';\n\nexport { SettingsSchema, type Settings } from './settings';\nexport { SetupSchema } from './setup';\n\nexport const settings = zodToSchema(SettingsSchema);\nexport const setup = zodToSchema(SetupSchema);\n","import { z } from '@walkeros/core/dev';\n\nexport const SettingsSchema = z.object({\n bucket: z.string().min(1).describe('GCS bucket name'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n credentials: z\n .union([\n z.string(),\n z.object({\n client_email: z.string().describe('Service account email'),\n private_key: z.string().describe('RSA private key in PEM format'),\n }),\n ])\n .optional()\n .describe(\n 'Service account JSON (string or object). Omit for ADC on Cloud Run/GKE (deprecated: use config.credentials)',\n ),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n projectId: z\n .string()\n .optional()\n .describe(\n 'GCP project that owns the bucket. Falls back to credentials.project_id then GOOGLE_CLOUD_PROJECT env',\n ),\n location: z\n .string()\n .default('EU')\n .describe('Geographic location (multi-region or region code)'),\n storageClass: z\n .enum(['STANDARD', 'NEARLINE', 'COLDLINE', 'ARCHIVE'])\n .default('STANDARD')\n .describe('Default storage class for objects'),\n versioning: z\n .boolean()\n .default(false)\n .describe('Enable object versioning at create time'),\n labels: z\n .record(z.string(), z.string())\n .optional()\n .describe('Labels for cost allocation'),\n kmsKeyName: z\n .string()\n .optional()\n .describe('Customer-managed encryption key for at-rest encryption'),\n // lifecycle rules intentionally omitted from this schema: the type is\n // deeply nested and not useful to autocomplete in MCP discovery for v1.\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: never mutates an existing bucket.',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\nexport * as env from './env';\n\n/** Cloud Run with ADC, serving assets byte-exact (file mode) */\nexport const cloudRunAdc: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n file: true,\n};\n\n/** Explicit service account for non-GCP environments (file mode) */\nexport const serviceAccount: Store.Config = {\n settings: {\n bucket: 'my-assets',\n prefix: 'public',\n },\n credentials: '$env.GCS_SA_KEY',\n file: true,\n};\n\n/** GCS as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n },\n};\n\nexport * as step from './step';\n","import type { Env } from '../types';\n\n/**\n * Mock env for the GCS store: an in-memory bucket behind `fetch`.\n *\n * It answers every request the store makes: the token exchange (service\n * account) or metadata token (ADC), the bucket existence check, and object\n * download, upload and delete. Objects live in a `Map` keyed by object name,\n * so a `get` after a `set` returns what was written. Any bucket exists.\n */\n\nconst TOKEN_BODY = { access_token: 'mock-access-token', expires_in: 3600 };\n\nfunction jsonResponse(body: unknown, status = 200): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\nfunction requestUrl(input: Parameters<typeof fetch>[0]): string {\n if (typeof input === 'string') return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction bodyBytes(body: RequestInit['body']): Uint8Array<ArrayBuffer> {\n if (typeof body === 'string') return new TextEncoder().encode(body);\n if (body instanceof Uint8Array) return new Uint8Array(body);\n return new Uint8Array();\n}\n\n/** Create a fresh in-memory GCS backend. */\nexport function createGcsFetch(): typeof fetch {\n const objects = new Map<string, Uint8Array<ArrayBuffer>>();\n\n return async (input, init) => {\n const url = new URL(requestUrl(input));\n const method = init?.method ?? 'GET';\n\n if (url.origin === 'https://oauth2.googleapis.com')\n return jsonResponse(TOKEN_BODY);\n if (url.hostname === 'metadata.google.internal')\n return jsonResponse(TOKEN_BODY);\n\n // Upload: POST /upload/storage/v1/b/<bucket>/o?uploadType=media&name=<key>\n if (url.pathname.startsWith('/upload/') && method === 'POST') {\n const name = url.searchParams.get('name') ?? '';\n objects.set(name, bodyBytes(init?.body));\n return jsonResponse({ name });\n }\n\n // Download: GET /download/storage/v1/b/<bucket>/o/<key>?alt=media\n const download = /^\\/download\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(\n url.pathname,\n );\n if (download) {\n const bytes = objects.get(decodeURIComponent(download[1]));\n if (!bytes) return jsonResponse({ error: { code: 404 } }, 404);\n return new Response(bytes, { status: 200 });\n }\n\n // Delete: DELETE /storage/v1/b/<bucket>/o/<key>\n const object = /^\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(url.pathname);\n if (object && method === 'DELETE') {\n objects.delete(decodeURIComponent(object[1]));\n return new Response(null, { status: 204 });\n }\n\n // Bucket existence: HEAD /storage/v1/b/<bucket>\n if (/^\\/storage\\/v1\\/b\\/[^/]+$/.test(url.pathname))\n return new Response(null, { status: 200 });\n\n return jsonResponse({ error: { code: 400 } }, 400);\n };\n}\n\n/** Each read of `fetch` is a fresh backend, so runs never share writes. */\nexport const push: Env = {\n get fetch() {\n return createGcsFetch();\n },\n};\n\n/** Every request goes through `fetch`; `args[0]` is the URL. */\nexport const simulation = ['fetch'];\n","import type { Flow } from '@walkeros/core';\n\n/** Read a file using Application Default Credentials (Cloud Run / GKE), file mode. */\nexport const readWithAdc: Flow.StepExample = {\n title: 'Read with ADC',\n description:\n 'Read object from GCS bucket using ADC (no credentials needed), bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped under the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to GCS path \"public/walker.js\"',\n in: {\n operation: 'get',\n key: 'walker.js',\n settings: { bucket: 'my-assets', prefix: 'public' },\n },\n out: [['get', 'public/walker.js', 'Bytes<...>']],\n};\n","import type { Hint } from '@walkeros/core';\n\nexport const hints: Hint.Hints = {\n 'file-mode': {\n text: 'Default mode is structured: values are StoreValue data round-tripped through the shared core codec and stored with Content-Type application/json, good for session state, lookups, and cached responses. Set config.file: true to persist raw bytes byte-exact with the real mime derived from the key (set accepts Uint8Array or string), the mode for serving assets such as walker.js via the file transformer. One store instance is exactly one mode. Do not pair file: true with a cache; the cache wraps structured stores and flow_validate flags the combination.',\n },\n};\n"],"mappings":"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","names":["z"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -51,7 +51,14 @@ interface Setup {
|
|
|
51
51
|
/** Labels for cost allocation. Optional. */
|
|
52
52
|
labels?: Record<string, string>;
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Injected dependencies (tests, simulate). Every GCS and token request goes
|
|
56
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
57
|
+
*/
|
|
58
|
+
interface Env extends Store.BaseEnv {
|
|
59
|
+
fetch?: typeof fetch;
|
|
60
|
+
}
|
|
61
|
+
type Types = Store.Types<GcsStoreSettings, Env, GcsStoreSettings, Setup, Credential<ServiceAccount>>;
|
|
55
62
|
|
|
56
63
|
declare const storeGcsInit: Store.Init<Types>;
|
|
57
64
|
|
|
@@ -72,6 +79,6 @@ type GcsStoreConfig = Store.Config<Types>;
|
|
|
72
79
|
* Idempotent: 409 means the bucket exists, drift is logged but never patched.
|
|
73
80
|
* Never auto-mutates an existing bucket.
|
|
74
81
|
*/
|
|
75
|
-
declare const setup: SetupFn<GcsStoreConfig,
|
|
82
|
+
declare const setup: SetupFn<GcsStoreConfig, Env>;
|
|
76
83
|
|
|
77
|
-
export { DEFAULT_SETUP, type GcsStoreSettings, type ServiceAccountCredentials, type Setup, type Types, storeGcsInit as default, setup, storeGcsInit };
|
|
84
|
+
export { DEFAULT_SETUP, type Env, type GcsStoreSettings, type ServiceAccountCredentials, type Setup, type Types, storeGcsInit as default, setup, storeGcsInit };
|
package/dist/index.d.ts
CHANGED
|
@@ -51,7 +51,14 @@ interface Setup {
|
|
|
51
51
|
/** Labels for cost allocation. Optional. */
|
|
52
52
|
labels?: Record<string, string>;
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Injected dependencies (tests, simulate). Every GCS and token request goes
|
|
56
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
57
|
+
*/
|
|
58
|
+
interface Env extends Store.BaseEnv {
|
|
59
|
+
fetch?: typeof fetch;
|
|
60
|
+
}
|
|
61
|
+
type Types = Store.Types<GcsStoreSettings, Env, GcsStoreSettings, Setup, Credential<ServiceAccount>>;
|
|
55
62
|
|
|
56
63
|
declare const storeGcsInit: Store.Init<Types>;
|
|
57
64
|
|
|
@@ -72,6 +79,6 @@ type GcsStoreConfig = Store.Config<Types>;
|
|
|
72
79
|
* Idempotent: 409 means the bucket exists, drift is logged but never patched.
|
|
73
80
|
* Never auto-mutates an existing bucket.
|
|
74
81
|
*/
|
|
75
|
-
declare const setup: SetupFn<GcsStoreConfig,
|
|
82
|
+
declare const setup: SetupFn<GcsStoreConfig, Env>;
|
|
76
83
|
|
|
77
|
-
export { DEFAULT_SETUP, type GcsStoreSettings, type ServiceAccountCredentials, type Setup, type Types, storeGcsInit as default, setup, storeGcsInit };
|
|
84
|
+
export { DEFAULT_SETUP, type Env, type GcsStoreSettings, type ServiceAccountCredentials, type Setup, type Types, storeGcsInit as default, setup, storeGcsInit };
|
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){let t;return async function(){return t&&Date.now()<t.expiresAt||(t=e?await async function(e){const t=Math.floor(Date.now()/1e3),n=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:t,exp:t+3600},e.private_key),r=await fetch(u,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${n}`});if(!r.ok)throw new Error(`Token exchange error: ${r.status}`);const o=await r.json();return{token:o.access_token,expiresAt:Date.now()+1e3*o.expires_in-f}}(e):await async function(){const e=await fetch(c,{headers:{"Metadata-Flavor":"Google"},signal:AbortSignal.timeout(5e3)});if(!e.ok)throw new Error(`Metadata server error: ${e.status}`);const t=await e.json();return{token:t.access_token,expiresAt:Date.now()+1e3*t.expires_in-f}}()),t.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=p(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=await s(),u=`${v}/storage/v1/b?project=${encodeURIComponent(a)}`,l=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),f=await fetch(u,{method:"POST",headers:{Authorization:`Bearer ${c}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(200===f.status||201===f.status)return n.info("setup: bucket created",{bucket:o.bucket,projectId:a,location:r.location}),{bucketCreated:!0};if(409!==f.status){const e=await async function(e){try{return await e.text()}catch{return""}}(f);throw new Error(`setup: bucket create failed (${f.status}): ${e}`)}return n.debug("setup: bucket already exists",{bucket:o.bucket}),await async function(e,t,n,r){const o=`${v}/storage/v1/b/${encodeURIComponent(e)}`;let i;try{const e=await fetch(o,{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)return void r.debug("setup: drift check failed (non-fatal)",{status:e.status});i=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 i.location&&void 0!==n.location&&i.location!==n.location&&r.warn("setup.drift",{field:"location",declared:n.location,actual:i.location});"string"==typeof i.storageClass&&void 0!==n.storageClass&&i.storageClass!==n.storageClass&&r.warn("setup.drift",{field:"storageClass",declared:n.storageClass,actual:i.storageClass});const a=n.versioning??!1,s=i.versioning?.enabled;"boolean"==typeof s&&s!==a&&r.warn("setup.drift",{field:"versioning",declared:a,actual:s});const c=i.iamConfiguration;if(c){const e=c.uniformBucketLevelAccess?.enabled;"boolean"==typeof e&&!0!==e&&r.warn("setup.drift",{field:"uniformBucketLevelAccess",declared:!0,actual:e});const t=c.publicAccessPrevention;"string"==typeof t&&"enforced"!==t&&r.warn("setup.drift",{field:"publicAccessPrevention",declared:"enforced",actual:t})}if(n.labels){const e=i.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,c,r,n),{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){const i=A.get(e);if(void 0!==i)return void await i;const a=(async()=>{let i;try{const t=await r(),n=`${C}/storage/v1/b/${encodeURIComponent(e)}`;i=await fetch(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===i.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 i.ok||o.debug("ensureBucketExists check failed (non-fatal)",{bucket:e,status:i.status}),!0})();A.set(e,a);try{await a}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=p(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=t.bucket,s=encodeURIComponent(i),c=e.id,{logger:u}=e,l=!0===e.config.file;function f(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return n+e;u.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=f(e);if(!t)return;let n;await S(i,c,r,o,u);try{const e=await o(),r=`${C}/download/storage/v1/b/${s}/o/${encodeURIComponent(t)}?alt=media`,i=await fetch(r,{headers:{Authorization:`Bearer ${e}`}});if(!i.ok)return;n=Buffer.from(await i.arrayBuffer())}catch{return}if(l)return n;try{return(0,a.deserializeStoreValue)(n)}catch(t){return void u.debug("structured decode failed, degrading to miss",{key:e,error:t instanceof Error?t.message:String(t)})}},async set(e,t){const n=f(e);if(!n)return;let p,g;if(l){if("string"==typeof t)p=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);p=O(t)}g=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 p=O((0,a.serializeStoreValue)(t)),g="application/json";await S(i,c,r,o,u);const d=await o(),y=`${C}/upload/storage/v1/b/${s}/o?uploadType=media&name=${encodeURIComponent(n)}`;await fetch(y,{method:"POST",headers:{Authorization:`Bearer ${d}`,"Content-Type":g},body:p})},async delete(e){const t=f(e);if(t){await S(i,c,r,o,u);try{const e=await o(),n=`${C}/storage/v1/b/${s}/o/${encodeURIComponent(t)}`;await fetch(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 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): 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 fetch(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 const getToken = createTokenProvider(creds);\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(bucketRaw, id, rawCreds, getToken, logger);\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 fetch(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(bucketRaw, id, rawCreds, getToken, logger);\n\n const token = await getToken();\n const url = `${GCS_BASE}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encodeURIComponent(gcsKey)}`;\n await fetch(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(bucketRaw, id, rawCreds, getToken, logger);\n\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}`;\n await fetch(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): 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)\n : await fetchMetadataToken();\n\n return cached.token;\n };\n}\n\nasync function fetchMetadataToken(): Promise<CachedToken> {\n const res = await fetch(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): 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 fetch(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 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, Store.BaseEnv> = async (\n context: LifecycleContext<GcsStoreConfig, Store.BaseEnv>,\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 const getToken = createTokenProvider(creds);\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 fetch(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);\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): Promise<void> {\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n let metadata: BucketMetadata;\n try {\n const res = await fetch(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){let
|
|
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): 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 fetch(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 const getToken = createTokenProvider(creds);\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(bucketRaw, id, rawCreds, getToken, logger);\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 fetch(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(bucketRaw, id, rawCreds, getToken, logger);\n\n const token = await getToken();\n const url = `${GCS_BASE}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encodeURIComponent(gcsKey)}`;\n await fetch(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(bucketRaw, id, rawCreds, getToken, logger);\n\n try {\n const token = await getToken();\n const url = `${GCS_BASE}/storage/v1/b/${bucket}/o/${encodeURIComponent(gcsKey)}`;\n await fetch(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): 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)\n : await fetchMetadataToken();\n\n return cached.token;\n };\n}\n\nasync function fetchMetadataToken(): Promise<CachedToken> {\n const res = await fetch(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): 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 fetch(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 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, Store.BaseEnv> = async (\n context: LifecycleContext<GcsStoreConfig, Store.BaseEnv>,\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 const getToken = createTokenProvider(creds);\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 fetch(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);\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): Promise<void> {\n const url = `${GCS_BASE}/storage/v1/b/${encodeURIComponent(bucket)}`;\n let metadata: BucketMetadata;\n try {\n const res = await fetch(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":";AACA,SAAS,qBAAqB,6BAA6B;;;ACD3D,SAAS,kBAAkB;AAK3B,IAAM,eACJ;AACF,IAAM,YAAY;AAClB,IAAM,QAAQ;AACd,IAAM,oBAAoB;AAOnB,SAAS,oBACd,aACA,UAAwB,OACT;AACf,MAAI;AAEJ,SAAO,eAAe,WAA4B;AAChD,QAAI,UAAU,KAAK,IAAI,IAAI,OAAO,UAAW,QAAO,OAAO;AAE3D,aAAS,cACL,MAAM,yBAAyB,aAAa,OAAO,IACnD,MAAM,mBAAmB,OAAO;AAEpC,WAAO,OAAO;AAAA,EAChB;AACF;AAEA,eAAe,mBAAmB,SAA6C;AAC7E,QAAM,MAAM,MAAM,QAAQ,cAAc;AAAA,IACtC,SAAS,EAAE,mBAAmB,SAAS;AAAA,IACvC,QAAQ,YAAY,QAAQ,GAAI;AAAA,EAClC,CAAC;AACD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,0BAA0B,IAAI,MAAM,EAAE;AAEnE,QAAM,OAAQ,MAAM,IAAI,KAAK;AAI7B,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa,MAAO;AAAA,EACnD;AACF;AAEA,eAAe,yBACb,OACA,SACsB;AACtB,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,MAAM;AAAA,IACV;AAAA,MACE,KAAK,MAAM;AAAA,MACX,OAAO;AAAA,MACP,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,MAAM;AAAA,IACb;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,MAAM,MAAM,QAAQ,WAAW;AAAA,IACnC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,IAC/D,MAAM,oEAAoE,GAAG;AAAA,EAC/E,CAAC;AACD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,yBAAyB,IAAI,MAAM,EAAE;AAElE,QAAM,OAAQ,MAAM,IAAI,KAAK;AAI7B,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa,MAAO;AAAA,EACnD;AACF;AAEA,SAAS,UAAU,OAAgC;AACjD,QAAM,MAAM,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAI;AAC7D,SAAO,IAAI,SAAS,WAAW;AACjC;AAEA,SAAS,QAAQ,SAAiB,YAA4B;AAC5D,QAAM,SAAS,UAAU,KAAK,UAAU,EAAE,KAAK,SAAS,KAAK,MAAM,CAAC,CAAC;AACrE,QAAM,OAAO,UAAU,KAAK,UAAU,OAAO,CAAC;AAC9C,QAAM,OAAO,WAAW,YAAY;AACpC,OAAK,OAAO,GAAG,MAAM,IAAI,IAAI,EAAE;AAC/B,SAAO,GAAG,MAAM,IAAI,IAAI,IAAI,KAAK,KAAK,YAAY,WAAW,CAAC;AAChE;;;AC7EA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAQA,SAAS,aAAa,OAAiD;AACrE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MACE,SAAS,KAAK,KACd,OAAO,MAAM,iBAAiB,YAC9B,OAAO,MAAM,gBAAgB,UAC7B;AACA,UAAM,EAAE,cAAc,aAAa,WAAW,IAAI;AAClD,WAAO,OAAO,eAAe,WACzB,EAAE,cAAc,aAAa,WAAW,IACxC,EAAE,cAAc,YAAY;AAAA,EAClC;AACA,SAAO;AACT;AAeO,SAAS,mBACd,QACA,QACiC;AACjC,MAAI,OAAO,gBAAgB,OAAW,QAAO,aAAa,OAAO,WAAW;AAE5E,QAAM,eAAe,OAAO,UAAU;AACtC,MAAI,iBAAiB,UAAa,QAAQ;AACxC,WAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACA,SAAO,aAAa,YAAY;AAClC;;;ACnEA,SAAS,oBAAoB;;;ACkBtB,SAAS,iBACdA,QACA,aACQ;AACR,MAAIA,OAAM,UAAW,QAAOA,OAAM;AAElC,QAAM,YAAY,gCAAgC,WAAW;AAC7D,MAAI,UAAW,QAAO;AAEtB,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,QAAS,QAAO;AAEpB,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAYA,SAAS,gCACP,aACoB;AACpB,MAAI,CAAC,YAAa,QAAO;AAEzB,MAAI,OAAO,gBAAgB,UAAU;AACnC,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC,QAAQ;AACN,aAAO;AAAA,IACT;AACA,WAAO,gBAAgB,QAAQ,YAAY;AAAA,EAC7C;AAEA,SAAO,gBAAgB,aAAa,YAAY;AAClD;AAEA,SAASC,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,gBACP,OACA,OACoB;AACpB,MAAI,CAACA,UAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,aAAa,MAAM,KAAK;AAC9B,SAAO,OAAO,eAAe,WAAW,aAAa;AACvD;;;AD9DA,IAAM,WAAW;AAQV,IAAM,gBAAuB;AAAA,EAClC,UAAU;AAAA,EACV,cAAc;AAAA,EACd,YAAY;AACd;AAiBO,IAAM,QAAsC,OACjD,YACG;AACH,QAAM,EAAE,QAAQ,OAAO,IAAI;AAC3B,QAAM,UAAU,aAAa,OAAO,OAAO,aAAa;AACxD,MAAI,CAAC,SAAS;AACZ,WAAO,MAAM,iDAAiD;AAC9D;AAAA,EACF;AAEA,QAAM,WAAW,OAAO;AACxB,MAAI,CAAC,YAAY,CAAC,SAAS,QAAQ;AACjC,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,QAAM,WAAW,mBAAmB,QAAQ,MAAM;AAClD,QAAM,YAAY,iBAAiB,SAAS,QAAQ;AACpD,QAAM,QAAQ,iBAAiB,QAAQ;AAGvC,QAAM,UAAU,QAAQ,KAAK,SAAS,QAAQ,OAAO,KAAK,SAAS;AACnE,QAAM,WAAW,oBAAoB,OAAO,OAAO;AACnD,QAAM,QAAQ,MAAM,SAAS;AAE7B,QAAM,YAAY,GAAG,QAAQ,yBAAyB,mBAAmB,SAAS,CAAC;AACnF,QAAM,OAAO,gBAAgB,SAAS,QAAQ,OAAO;AAErD,QAAM,YAAY,MAAM,QAAQ,WAAW;AAAA,IACzC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AAED,MAAI,UAAU,WAAW,OAAO,UAAU,WAAW,KAAK;AACxD,WAAO,KAAK,yBAAyB;AAAA,MACnC,QAAQ,SAAS;AAAA,MACjB;AAAA,MACA,UAAU,QAAQ;AAAA,IACpB,CAAC;AACD,WAAO,EAAE,eAAe,KAAK;AAAA,EAC/B;AAEA,MAAI,UAAU,WAAW,KAAK;AAC5B,UAAM,OAAO,MAAM,SAAS,SAAS;AACrC,UAAM,IAAI;AAAA,MACR,gCAAgC,UAAU,MAAM,MAAM,IAAI;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO,MAAM,gCAAgC,EAAE,QAAQ,SAAS,OAAO,CAAC;AACxE,QAAM,YAAY,SAAS,QAAQ,OAAO,SAAS,QAAQ,OAAO;AAClE,SAAO,EAAE,eAAe,MAAM;AAChC;AAgBA,SAAS,gBAAgB,QAAgB,SAA4B;AACnE,QAAM,OAAmB;AAAA,IACvB,MAAM;AAAA,IACN,UAAU,QAAQ;AAAA,IAClB,cAAc,QAAQ;AAAA,IACtB,YAAY,EAAE,SAAS,QAAQ,cAAc,MAAM;AAAA,IACnD,kBAAkB;AAAA,MAChB,0BAA0B,EAAE,SAAS,KAAK;AAAA,MAC1C,wBAAwB;AAAA,IAC1B;AAAA,EACF;AACA,MAAI,QAAQ,OAAQ,MAAK,SAAS,QAAQ;AAC1C,MAAI,QAAQ,YAAY;AACtB,SAAK,aAAa,EAAE,mBAAmB,QAAQ,WAAW;AAAA,EAC5D;AACA,MAAI,QAAQ,UAAW,MAAK,YAAY,QAAQ;AAChD,SAAO;AACT;AAEA,SAAS,iBACP,aACuC;AACvC,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI,OAAO,gBAAgB,UAAU;AACnC,UAAM,SAAkB,KAAK,MAAM,WAAW;AAC9C,QAAI,sBAAsB,MAAM,EAAG,QAAO;AAC1C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,sBACP,OACoC;AACpC,MAAI,CAACC,UAAS,KAAK,EAAG,QAAO;AAC7B,SACE,OAAO,MAAM,iBAAiB,YAC9B,OAAO,MAAM,gBAAgB;AAEjC;AAEA,eAAe,SAAS,KAAgC;AACtD,MAAI;AACF,WAAO,MAAM,IAAI,KAAK;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaA,eAAe,YACb,QACA,OACA,SACA,QACA,SACe;AACf,QAAM,MAAM,GAAG,QAAQ,iBAAiB,mBAAmB,MAAM,CAAC;AAClE,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,QAAQ,KAAK;AAAA,MAC7B,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,IAC9C,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,aAAO,MAAM,yCAAyC;AAAA,QACpD,QAAQ,IAAI;AAAA,MACd,CAAC;AACD;AAAA,IACF;AACA,UAAM,SAAkB,MAAM,IAAI,KAAK;AACvC,eAAW,mBAAmB,MAAM;AAAA,EACtC,SAAS,KAAK;AACZ,WAAO,MAAM,yCAAyC;AAAA,MACpD,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACxD,CAAC;AACD;AAAA,EACF;AAEA,MACE,OAAO,SAAS,aAAa,YAC7B,QAAQ,aAAa,UACrB,SAAS,aAAa,QAAQ,UAC9B;AACA,WAAO,KAAK,eAAe;AAAA,MACzB,OAAO;AAAA,MACP,UAAU,QAAQ;AAAA,MAClB,QAAQ,SAAS;AAAA,IACnB,CAAC;AAAA,EACH;AAEA,MACE,OAAO,SAAS,iBAAiB,YACjC,QAAQ,iBAAiB,UACzB,SAAS,iBAAiB,QAAQ,cAClC;AACA,WAAO,KAAK,eAAe;AAAA,MACzB,OAAO;AAAA,MACP,UAAU,QAAQ;AAAA,MAClB,QAAQ,SAAS;AAAA,IACnB,CAAC;AAAA,EACH;AAEA,QAAM,qBAAqB,QAAQ,cAAc;AACjD,QAAM,mBAAmB,SAAS,YAAY;AAC9C,MACE,OAAO,qBAAqB,aAC5B,qBAAqB,oBACrB;AACA,WAAO,KAAK,eAAe;AAAA,MACzB,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,QAAM,MAAM,SAAS;AACrB,MAAI,KAAK;AACP,UAAM,gBAAgB,IAAI,0BAA0B;AACpD,QAAI,OAAO,kBAAkB,aAAa,kBAAkB,MAAM;AAChE,aAAO,KAAK,eAAe;AAAA,QACzB,OAAO;AAAA,QACP,UAAU;AAAA,QACV,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,UAAM,YAAY,IAAI;AACtB,QAAI,OAAO,cAAc,YAAY,cAAc,YAAY;AAC7D,aAAO,KAAK,eAAe;AAAA,QACzB,OAAO;AAAA,QACP,UAAU;AAAA,QACV,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,QAAQ,QAAQ;AAClB,UAAM,eAAe,SAAS,UAAU,CAAC;AACzC,QAAI,CAAC,YAAY,QAAQ,QAAQ,YAAY,GAAG;AAC9C,aAAO,KAAK,eAAe;AAAA,QACzB,OAAO;AAAA,QACP,UAAU,QAAQ;AAAA,QAClB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,YACP,GACA,GACS;AACT,QAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,QAAM,QAAQ,OAAO,KAAK,CAAC;AAC3B,MAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC1C,aAAW,OAAO,OAAO;AACvB,QAAI,EAAE,GAAG,MAAM,EAAE,GAAG,EAAG,QAAO;AAAA,EAChC;AACA,SAAO;AACT;AAEA,SAASA,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,mBAAmB,OAAgC;AAC1D,MAAI,CAACA,UAAS,KAAK,EAAG,QAAO,CAAC;AAC9B,QAAM,MAAsB,CAAC;AAC7B,MAAI,OAAO,MAAM,aAAa,SAAU,KAAI,WAAW,MAAM;AAC7D,MAAI,OAAO,MAAM,iBAAiB,UAAU;AAC1C,QAAI,eAAe,MAAM;AAAA,EAC3B;AACA,MAAIA,UAAS,MAAM,UAAU,GAAG;AAC9B,UAAM,UAAU,MAAM,WAAW;AACjC,QAAI,OAAO,YAAY,WAAW;AAChC,UAAI,aAAa,EAAE,QAAQ;AAAA,IAC7B;AAAA,EACF;AACA,MAAIA,UAAS,MAAM,gBAAgB,GAAG;AACpC,UAAM,MAA0C,CAAC;AACjD,UAAM,UAAU,MAAM,iBAAiB;AACvC,QAAIA,UAAS,OAAO,KAAK,OAAO,QAAQ,YAAY,WAAW;AAC7D,UAAI,2BAA2B,EAAE,SAAS,QAAQ,QAAQ;AAAA,IAC5D;AACA,UAAM,MAAM,MAAM,iBAAiB;AACnC,QAAI,OAAO,QAAQ,SAAU,KAAI,yBAAyB;AAC1D,QAAI,mBAAmB;AAAA,EACzB;AACA,MAAIA,UAAS,MAAM,MAAM,GAAG;AAC1B,UAAM,SAAiC,CAAC;AACxC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AACjD,UAAI,OAAO,MAAM,SAAU,QAAO,CAAC,IAAI;AAAA,IACzC;AACA,QAAI,SAAS;AAAA,EACf;AACA,SAAO;AACT;;;AHlTA,IAAMC,YAAW;AAEjB,IAAM,oBAAoB;AAC1B,IAAM,uBAAuB;AAK7B,IAAM,cAAgD;AAAA,EACpD,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AACP;AAEA,SAAS,YAAY,KAAqB;AACxC,QAAM,MAAM,IAAI,YAAY,GAAG;AAC/B,MAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,EAAG,QAAO;AAC9C,QAAM,MAAM,IAAI,MAAM,MAAM,CAAC,EAAE,YAAY;AAC3C,SAAO,YAAY,GAAG,KAAK;AAC7B;AAUA,IAAM,oBAAmD,oBAAI,IAAI;AAYjE,SAAS,WAAW,KAAsB;AACxC,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AAChE,SAAO,CAAC,IAAI,MAAM,OAAO,EAAE,SAAS,IAAI;AAC1C;AAEA,SAAS,gBAAgB,QAAyB;AAChD,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,QAAQ,cAAc,EAAE;AAC/C,SAAO,UAAU,UAAU,MAAM;AACnC;AAEA,SAASC,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAASC,uBACP,OACoC;AACpC,MAAI,CAACD,UAAS,KAAK,EAAG,QAAO;AAC7B,SACE,OAAO,MAAM,iBAAiB,YAC9B,OAAO,MAAM,gBAAgB;AAEjC;AAEA,SAASE,kBACP,aACuC;AACvC,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI,OAAO,gBAAgB,UAAU;AACnC,UAAM,SAAkB,KAAK,MAAM,WAAW;AAC9C,QAAID,uBAAsB,MAAM,EAAG,QAAO;AAC1C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASA,SAAS,aAAa,KAA0C;AAC9D,QAAM,MAAM,IAAI,YAAY,IAAI,UAAU;AAC1C,QAAM,OAAO,IAAI,WAAW,GAAG;AAC/B,OAAK,IAAI,GAAG;AACZ,SAAO;AACT;AAEA,SAAS,kBACP,UACsC;AACtC,MACE,CAAC,YACD,OAAO,SAAS,WAAW,YAC3B,SAAS,OAAO,WAAW,GAC3B;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAAS,2BACP,aACQ;AACR,MAAI;AACF,WAAO,iBAAiB,CAAC,GAAG,WAAW;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,eAAe,mBACb,QACA,IACA,aACA,UACA,QACA,SACe;AACf,QAAM,WAAW,kBAAkB,IAAI,MAAM;AAC7C,MAAI,aAAa,QAAW;AAC1B,UAAM;AACN;AAAA,EACF;AAEA,QAAM,WAAW,YAA8B;AAC7C,QAAI;AACJ,QAAI;AACF,YAAM,QAAQ,MAAM,SAAS;AAC7B,YAAM,MAAM,GAAGE,SAAQ,iBAAiB,mBAAmB,MAAM,CAAC;AAClE,YAAM,MAAM,QAAQ,KAAK;AAAA,QACvB,QAAQ;AAAA,QACR,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,MAC9C,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,MAAM,+CAA+C;AAAA,QAC1D;AAAA,QACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,CAAC;AACD,aAAO;AAAA,IACT;AAEA,QAAI,IAAI,WAAW,KAAK;AACtB,YAAM,YAAY,2BAA2B,WAAW;AACxD,YAAM,IAAI;AAAA,QACR,yBAAyB,MAAM,eAAe,SAAS,+BAA+B,EAAE;AAAA,MAC1F;AAAA,IACF;AACA,QAAI,CAAC,IAAI,IAAI;AACX,aAAO,MAAM,+CAA+C;AAAA,QAC1D;AAAA,QACA,QAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG;AAEH,oBAAkB,IAAI,QAAQ,OAAO;AACrC,MAAI;AACF,UAAM;AAAA,EACR,SAAS,KAAK;AAEZ,sBAAkB,OAAO,MAAM;AAC/B,UAAM;AAAA,EACR;AACF;AAEO,IAAM,eAAkC,CAAC,YAAY;AAC1D,oBAAkB,QAAQ,OAAO,QAAQ;AACzC,QAAM,WAA6B,QAAQ,OAAO;AAClD,QAAM,SAAS,gBAAgB,SAAS,MAAM;AAC9C,QAAM,WAAW,mBAAmB,QAAQ,QAAQ,QAAQ,MAAM;AAClE,QAAM,QAAQD,kBAAiB,QAAQ;AAGvC,QAAM,UAAU,QAAQ,KAAK,SAAS,QAAQ,OAAO,KAAK,SAAS;AACnE,QAAM,WAAW,oBAAoB,OAAO,OAAO;AACnD,QAAM,YAAY,SAAS;AAC3B,QAAM,SAAS,mBAAmB,SAAS;AAC3C,QAAM,KAAK,QAAQ;AACnB,QAAM,EAAE,OAAO,IAAI;AAInB,QAAM,WAAW,QAAQ,OAAO,SAAS;AAEzC,WAAS,WAAW,KAAiC;AACnD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,aAAO,KAAK,wBAAwB,EAAE,IAAI,CAAC;AAC3C,aAAO;AAAA,IACT;AACA,WAAO,SAAS;AAAA,EAClB;AAEA,QAAM,SAA8B;AAAA,IAClC;AAAA,IACA,KAAK,QAAQ,OAAO;AAAA,IACpB,IAAI,QAAQ,OAAO;AAAA,IACnB,QAAQ,QAAQ,OAAO;AAAA;AAAA,IAEvB,MAAM,QAAQ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvB;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IAEA,MAAM,IAAI,KAAoD;AAC5D,YAAM,SAAS,WAAW,GAAG;AAC7B,UAAI,CAAC,OAAQ,QAAO;AAEpB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,cAAM,QAAQ,MAAM,SAAS;AAC7B,cAAM,MAAM,GAAGC,SAAQ,0BAA0B,MAAM,MAAM,mBAAmB,MAAM,CAAC;AACvF,cAAM,MAAM,MAAM,QAAQ,KAAK;AAAA,UAC7B,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,QAC9C,CAAC;AACD,YAAI,CAAC,IAAI,GAAI,QAAO;AACpB,gBAAQ,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC;AAAA,MAC7C,QAAQ;AACN,eAAO;AAAA,MACT;AAKA,UAAI,SAAU,QAAO;AACrB,UAAI;AACF,eAAO,sBAAsB,KAAK;AAAA,MACpC,SAAS,KAAK;AACZ,eAAO,MAAM,+CAA+C;AAAA,UAC1D;AAAA,UACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD,CAAC;AACD,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,IAAI,KAAa,OAAwC;AAC7D,YAAM,SAAS,WAAW,GAAG;AAC7B,UAAI,CAAC,OAAQ;AAEb,UAAI;AACJ,UAAI;AACJ,UAAI,UAAU;AAEZ,YAAI,OAAO,UAAU,UAAU;AAC7B,iBAAO,aAAa,OAAO,KAAK,KAAK,CAAC;AAAA,QACxC,WAAW,iBAAiB,YAAY;AACtC,iBAAO,aAAa,KAAK;AAAA,QAC3B,OAAO;AACL,gBAAM,IAAI;AAAA,YACR,4EACE,OAAO;AAAA,UACX;AAAA,QACF;AACA,sBAAc,YAAY,GAAG;AAAA,MAC/B,OAAO;AAEL,eAAO,aAAa,oBAAoB,KAAK,CAAC;AAC9C,sBAAc;AAAA,MAChB;AAEA,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,QAAQ,MAAM,SAAS;AAC7B,YAAM,MAAM,GAAGA,SAAQ,wBAAwB,MAAM,4BAA4B,mBAAmB,MAAM,CAAC;AAC3G,YAAM,QAAQ,KAAK;AAAA,QACjB,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,KAAK;AAAA,UAC9B,gBAAgB;AAAA,QAClB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,OAAO,KAA4B;AACvC,YAAM,SAAS,WAAW,GAAG;AAC7B,UAAI,CAAC,OAAQ;AAEb,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AACF,cAAM,QAAQ,MAAM,SAAS;AAC7B,cAAM,MAAM,GAAGA,SAAQ,iBAAiB,MAAM,MAAM,mBAAmB,MAAM,CAAC;AAC9E,cAAM,QAAQ,KAAK;AAAA,UACjB,QAAQ;AAAA,UACR,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG;AAAA,QAC9C,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","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
|
},
|
|
@@ -112,6 +113,19 @@
|
|
|
112
113
|
},
|
|
113
114
|
"file": true
|
|
114
115
|
},
|
|
116
|
+
"env": {
|
|
117
|
+
"createGcsFetch": {
|
|
118
|
+
"$code": "function f(){const e=new Map;return async(t,r)=>{const s=new URL(function(e){return\"string\"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),o=r?.method??\"GET\";if(\"https://oauth2.googleapis.com\"===s.origin)return g(u);if(\"metadata.google.internal\"===s.hostname)return g(u);if(s.pathname.startsWith(\"/upload/\")&&\"POST\"===o){const t=s.searchParams.get(\"name\")??\"\";return e.set(t,(n=r?.body,\"string\"==typeof n?(new TextEncoder).encode(n):n instanceof Uint8Array?new Uint8Array(n):new Uint8Array)),g({name:t})}var n;const i=/^\\/download\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(s.pathname);if(i){const t=e.get(decodeURIComponent(i[1]));return t?new Response(t,{status:200}):g({error:{code:404}},404)}const a=/^\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(s.pathname);return a&&\"DELETE\"===o?(e.delete(decodeURIComponent(a[1])),new Response(null,{status:204})):/^\\/storage\\/v1\\/b\\/[^/]+$/.test(s.pathname)?new Response(null,{status:200}):g({error:{code:400}},400)}}"
|
|
119
|
+
},
|
|
120
|
+
"push": {
|
|
121
|
+
"fetch": {
|
|
122
|
+
"$code": "async(t,r)=>{const s=new URL(function(e){return\"string\"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),o=r?.method??\"GET\";if(\"https://oauth2.googleapis.com\"===s.origin)return g(u);if(\"metadata.google.internal\"===s.hostname)return g(u);if(s.pathname.startsWith(\"/upload/\")&&\"POST\"===o){const t=s.searchParams.get(\"name\")??\"\";return e.set(t,(n=r?.body,\"string\"==typeof n?(new TextEncoder).encode(n):n instanceof Uint8Array?new Uint8Array(n):new Uint8Array)),g({name:t})}var n;const i=/^\\/download\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(s.pathname);if(i){const t=e.get(decodeURIComponent(i[1]));return t?new Response(t,{status:200}):g({error:{code:404}},404)}const a=/^\\/storage\\/v1\\/b\\/[^/]+\\/o\\/(.+)$/.exec(s.pathname);return a&&\"DELETE\"===o?(e.delete(decodeURIComponent(a[1])),new Response(null,{status:204})):/^\\/storage\\/v1\\/b\\/[^/]+$/.test(s.pathname)?new Response(null,{status:200}):g({error:{code:400}},400)}"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"simulation": [
|
|
126
|
+
"fetch"
|
|
127
|
+
]
|
|
128
|
+
},
|
|
115
129
|
"serviceAccount": {
|
|
116
130
|
"settings": {
|
|
117
131
|
"bucket": "my-assets",
|
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": {
|