@walkeros/server-store-s3 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 +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +15 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
# @walkeros/server-store-s3
|
|
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;
|
|
@@ -30,6 +30,28 @@ declare namespace index$1 {
|
|
|
30
30
|
export { type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, type index$1_Setup as Setup, index$1_SetupSchema as SetupSchema, index$1_settings as settings, index$1_setup as setup };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Injected dependencies (tests, simulate). The S3 client sends every request
|
|
35
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
36
|
+
*/
|
|
37
|
+
interface Env extends Store.BaseEnv {
|
|
38
|
+
fetch?: typeof fetch;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Create a fresh in-memory S3 backend. */
|
|
42
|
+
declare function createS3Fetch(): typeof fetch;
|
|
43
|
+
/** Each read of `fetch` is a fresh backend, so runs never share writes. */
|
|
44
|
+
declare const push: Env;
|
|
45
|
+
/** Every request goes through `fetch`; `args[0]` is the signed URL. */
|
|
46
|
+
declare const simulation: string[];
|
|
47
|
+
|
|
48
|
+
declare const env_createS3Fetch: typeof createS3Fetch;
|
|
49
|
+
declare const env_push: typeof push;
|
|
50
|
+
declare const env_simulation: typeof simulation;
|
|
51
|
+
declare namespace env {
|
|
52
|
+
export { env_createS3Fetch as createS3Fetch, env_push as push, env_simulation as simulation };
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
/** Read an object from an AWS S3 bucket (file mode, byte-exact). */
|
|
34
56
|
declare const readAwsS3: Flow.StepExample;
|
|
35
57
|
/** Key is scoped to the configured prefix subdirectory. */
|
|
@@ -49,11 +71,12 @@ declare const structuredKv: Store.Config;
|
|
|
49
71
|
declare const r2Bucket: Store.Config;
|
|
50
72
|
|
|
51
73
|
declare const index_awsAssets: typeof awsAssets;
|
|
74
|
+
declare const index_env: typeof env;
|
|
52
75
|
declare const index_r2Bucket: typeof r2Bucket;
|
|
53
76
|
declare const index_step: typeof step;
|
|
54
77
|
declare const index_structuredKv: typeof structuredKv;
|
|
55
78
|
declare namespace index {
|
|
56
|
-
export { index_awsAssets as awsAssets, index_r2Bucket as r2Bucket, index_step as step, index_structuredKv as structuredKv };
|
|
79
|
+
export { index_awsAssets as awsAssets, index_env as env, index_r2Bucket as r2Bucket, index_step as step, index_structuredKv as structuredKv };
|
|
57
80
|
}
|
|
58
81
|
|
|
59
82
|
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;
|
|
@@ -30,6 +30,28 @@ declare namespace index$1 {
|
|
|
30
30
|
export { type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, type index$1_Setup as Setup, index$1_SetupSchema as SetupSchema, index$1_settings as settings, index$1_setup as setup };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Injected dependencies (tests, simulate). The S3 client sends every request
|
|
35
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
36
|
+
*/
|
|
37
|
+
interface Env extends Store.BaseEnv {
|
|
38
|
+
fetch?: typeof fetch;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Create a fresh in-memory S3 backend. */
|
|
42
|
+
declare function createS3Fetch(): typeof fetch;
|
|
43
|
+
/** Each read of `fetch` is a fresh backend, so runs never share writes. */
|
|
44
|
+
declare const push: Env;
|
|
45
|
+
/** Every request goes through `fetch`; `args[0]` is the signed URL. */
|
|
46
|
+
declare const simulation: string[];
|
|
47
|
+
|
|
48
|
+
declare const env_createS3Fetch: typeof createS3Fetch;
|
|
49
|
+
declare const env_push: typeof push;
|
|
50
|
+
declare const env_simulation: typeof simulation;
|
|
51
|
+
declare namespace env {
|
|
52
|
+
export { env_createS3Fetch as createS3Fetch, env_push as push, env_simulation as simulation };
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
/** Read an object from an AWS S3 bucket (file mode, byte-exact). */
|
|
34
56
|
declare const readAwsS3: Flow.StepExample;
|
|
35
57
|
/** Key is scoped to the configured prefix subdirectory. */
|
|
@@ -49,11 +71,12 @@ declare const structuredKv: Store.Config;
|
|
|
49
71
|
declare const r2Bucket: Store.Config;
|
|
50
72
|
|
|
51
73
|
declare const index_awsAssets: typeof awsAssets;
|
|
74
|
+
declare const index_env: typeof env;
|
|
52
75
|
declare const index_r2Bucket: typeof r2Bucket;
|
|
53
76
|
declare const index_step: typeof step;
|
|
54
77
|
declare const index_structuredKv: typeof structuredKv;
|
|
55
78
|
declare namespace index {
|
|
56
|
-
export { index_awsAssets as awsAssets, index_r2Bucket as r2Bucket, index_step as step, index_structuredKv as structuredKv };
|
|
79
|
+
export { index_awsAssets as awsAssets, index_env as env, index_r2Bucket as r2Bucket, index_step as step, index_structuredKv as structuredKv };
|
|
57
80
|
}
|
|
58
81
|
|
|
59
82
|
declare const hints: Hint.Hints;
|
package/dist/dev.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=Object.defineProperty,s=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,
|
|
1
|
+
"use strict";var e,t=Object.defineProperty,s=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,n=Object.prototype.hasOwnProperty,o=(e,s)=>{for(var r in s)t(e,r,{get:s[r],enumerable:!0})},i={};o(i,{examples:()=>h,hints:()=>A,schemas:()=>a}),module.exports=(e=i,((e,o,i,a)=>{if(o&&"object"==typeof o||"function"==typeof o)for(let c of r(o))n.call(e,c)||c===i||t(e,c,{get:()=>o[c],enumerable:!(a=s(o,c))||a.enumerable});return e})(t({},"__esModule",{value:!0}),e));var a={};o(a,{SettingsSchema:()=>l,SetupSchema:()=>d,settings:()=>g,setup:()=>f});var c=require("@walkeros/core/dev"),u=require("@walkeros/core/dev"),l=u.z.object({bucket:u.z.string().min(1).describe("S3 bucket name"),endpoint:u.z.string().url().describe("S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)"),accessKeyId:u.z.string().min(1).describe("S3 access key ID"),secretAccessKey:u.z.string().min(1).describe("S3 secret access key"),region:u.z.string().default("auto").describe("AWS region for SigV4 signing"),prefix:u.z.string().optional().describe("Key prefix prepended to all store keys for scoping")}),p=require("@walkeros/core/dev"),d=p.z.object({region:p.z.string().default("eu-central-1").describe('Region the bucket is created in. Defaults to settings.region when concrete (not "auto"), otherwise eu-central-1.')}).describe('Provisioning options for "walkeros setup store.<id>". Idempotent: only the bucket is created. Encryption, public-access block, versioning, lifecycle rules, and tags are not applied here (s3mini does not expose those operations); configure them once via the AWS Console or "aws s3api".'),g=(0,c.zodToSchema)(l),f=(0,c.zodToSchema)(d),h={};o(h,{awsAssets:()=>E,env:()=>y,r2Bucket:()=>j,step:()=>S,structuredKv:()=>K});var y={};function m(){const e=new Map;return async(t,s)=>{const{pathname:r}=new URL(function(e){return"string"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),n=s?.method??"GET";if(r.replace(/\/+$/,"").split("/").length<=2)return new Response(null,{status:200});if("PUT"===n)return e.set(r,(o=s?.body,"string"==typeof o?(new TextEncoder).encode(o):o instanceof Uint8Array?new Uint8Array(o):new Uint8Array)),new Response(null,{status:200,headers:{etag:'"mock"'}});var o;if("DELETE"===n)return e.delete(r),new Response(null,{status:204});const i=e.get(r);return i?new Response(i,{status:200}):new Response(null,{status:404})}}o(y,{createS3Fetch:()=>m,push:()=>w,simulation:()=>b});var w={get fetch(){return m()}},b=["fetch"],S={};o(S,{prefixScoping:()=>v,readAwsS3:()=>k});var k={title:"Read from S3",description:"Read object from S3 and receive its raw 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 S3 path "public/walker.js"',in:{operation:"get",key:"walker.js",settings:{bucket:"my-assets",prefix:"public"}},out:[["get","public/walker.js","Bytes<...>"]]},E={settings:{bucket:"my-assets",endpoint:"https://s3.eu-west-1.amazonaws.com",accessKeyId:"$env.S3_ACCESS_KEY",secretAccessKey:"$env.S3_SECRET_KEY",region:"eu-west-1",prefix:"public"},file:!0},K={settings:{bucket:"my-state",endpoint:"https://s3.eu-west-1.amazonaws.com",accessKeyId:"$env.S3_ACCESS_KEY",secretAccessKey:"$env.S3_SECRET_KEY",region:"eu-west-1"}},j={settings:{bucket:"my-bucket",endpoint:"https://ACCOUNT_ID.r2.cloudflarestorage.com",accessKeyId:"$env.R2_ACCESS_KEY",secretAccessKey:"$env.R2_SECRET_KEY"},file:!0},A={"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, type Setup } 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('S3 bucket name'),\n endpoint: z\n .string()\n .url()\n .describe(\n 'S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)',\n ),\n accessKeyId: z.string().min(1).describe('S3 access key ID'),\n secretAccessKey: z.string().min(1).describe('S3 secret access key'),\n region: z.string().default('auto').describe('AWS region for SigV4 signing'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n region: z\n .string()\n .default('eu-central-1')\n .describe(\n 'Region the bucket is created in. Defaults to settings.region when concrete (not \"auto\"), otherwise eu-central-1.',\n ),\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: only the bucket is created. Encryption, public-access block, versioning, lifecycle rules, and tags are not applied here (s3mini does not expose those operations); configure them once via the AWS Console or \"aws s3api\".',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\n/** AWS S3 bucket for serving static files byte-exact (file mode) */\nexport const awsAssets: Store.Config = {\n settings: {\n bucket: 'my-assets',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n prefix: 'public',\n },\n file: true,\n};\n\n/** S3 as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n },\n};\n\n/** Cloudflare R2 bucket, no egress fees */\nexport const r2Bucket: Store.Config = {\n settings: {\n bucket: 'my-bucket',\n endpoint: 'https://ACCOUNT_ID.r2.cloudflarestorage.com',\n accessKeyId: '$env.R2_ACCESS_KEY',\n secretAccessKey: '$env.R2_SECRET_KEY',\n },\n file: true,\n};\n\nexport * as step from './step';\n","import type { Flow } from '@walkeros/core';\n\n/** Read an object from an AWS S3 bucket (file mode, byte-exact). */\nexport const readAwsS3: Flow.StepExample = {\n title: 'Read from S3',\n description: 'Read object from S3 and receive its raw bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped to the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to S3 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,gBAAgB;AAAA,EACnD,UAAU,aACP,OAAO,EACP,IAAI,EACJ;AAAA,IACC;AAAA,EACF;AAAA,EACF,aAAa,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,kBAAkB;AAAA,EAC1D,iBAAiB,aAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,sBAAsB;AAAA,EAClE,QAAQ,aAAE,OAAO,EAAE,QAAQ,MAAM,EAAE,SAAS,8BAA8B;AAAA,EAC1E,QAAQ,aACL,OAAO,EACP,SAAS,EACT,SAAS,oDAAoD;AAClE,CAAC;;;ACjBD,IAAAC,cAAkB;AAEX,IAAM,cAAc,cACxB,OAAO;AAAA,EACN,QAAQ,cACL,OAAO,EACP,QAAQ,cAAc,EACtB;AAAA,IACC;AAAA,EACF;AACJ,CAAC,EACA;AAAA,EACC;AACF;;;AFNK,IAAM,eAAW,yBAAY,cAAc;AAC3C,IAAM,YAAQ,yBAAY,WAAW;;;AGR5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,YAA8B;AAAA,EACzC,OAAO;AAAA,EACP,aAAa;AAAA,EACb,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;;;
|
|
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, type Setup } 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('S3 bucket name'),\n endpoint: z\n .string()\n .url()\n .describe(\n 'S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)',\n ),\n accessKeyId: z.string().min(1).describe('S3 access key ID'),\n secretAccessKey: z.string().min(1).describe('S3 secret access key'),\n region: z.string().default('auto').describe('AWS region for SigV4 signing'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n region: z\n .string()\n .default('eu-central-1')\n .describe(\n 'Region the bucket is created in. Defaults to settings.region when concrete (not \"auto\"), otherwise eu-central-1.',\n ),\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: only the bucket is created. Encryption, public-access block, versioning, lifecycle rules, and tags are not applied here (s3mini does not expose those operations); configure them once via the AWS Console or \"aws s3api\".',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\nexport * as env from './env';\n\n/** AWS S3 bucket for serving static files byte-exact (file mode) */\nexport const awsAssets: Store.Config = {\n settings: {\n bucket: 'my-assets',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n prefix: 'public',\n },\n file: true,\n};\n\n/** S3 as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n },\n};\n\n/** Cloudflare R2 bucket, no egress fees */\nexport const r2Bucket: Store.Config = {\n settings: {\n bucket: 'my-bucket',\n endpoint: 'https://ACCOUNT_ID.r2.cloudflarestorage.com',\n accessKeyId: '$env.R2_ACCESS_KEY',\n secretAccessKey: '$env.R2_SECRET_KEY',\n },\n file: true,\n};\n\nexport * as step from './step';\n","import type { Env } from '../types';\n\n/**\n * Mock env for the S3 store: an in-memory bucket behind `fetch`.\n *\n * The store's S3 client (s3mini) signs each request and sends it through the\n * injected `fetch`. This mock answers the bucket existence check at start\n * and object get, put and delete. Objects live in a `Map` keyed by the\n * request path, so a `get` after a `set` returns what was written. Any\n * bucket exists; no credentials are checked.\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 S3 backend. */\nexport function createS3Fetch(): typeof fetch {\n const objects = new Map<string, Uint8Array<ArrayBuffer>>();\n\n return async (input, init) => {\n const { pathname } = new URL(requestUrl(input));\n const method = init?.method ?? 'GET';\n // Path style: /<bucket> for the bucket, /<bucket>/<key> for an object.\n const isBucket = pathname.replace(/\\/+$/, '').split('/').length <= 2;\n\n if (isBucket) return new Response(null, { status: 200 });\n\n if (method === 'PUT') {\n objects.set(pathname, bodyBytes(init?.body));\n return new Response(null, { status: 200, headers: { etag: '\"mock\"' } });\n }\n if (method === 'DELETE') {\n objects.delete(pathname);\n return new Response(null, { status: 204 });\n }\n\n const bytes = objects.get(pathname);\n if (!bytes) return new Response(null, { status: 404 });\n return new Response(bytes, { status: 200 });\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 createS3Fetch();\n },\n};\n\n/** Every request goes through `fetch`; `args[0]` is the signed URL. */\nexport const simulation = ['fetch'];\n","import type { Flow } from '@walkeros/core';\n\n/** Read an object from an AWS S3 bucket (file mode, byte-exact). */\nexport const readAwsS3: Flow.StepExample = {\n title: 'Read from S3',\n description: 'Read object from S3 and receive its raw bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped to the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to S3 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,s)=>{for(var r in s)e(t,r,{get:s[r],enumerable:!0})},s={};t(s,{SettingsSchema:()=>o,SetupSchema:()=>
|
|
1
|
+
var e=Object.defineProperty,t=(t,s)=>{for(var r in s)e(t,r,{get:s[r],enumerable:!0})},s={};t(s,{SettingsSchema:()=>o,SetupSchema:()=>a,settings:()=>c,setup:()=>u});import{zodToSchema as r}from"@walkeros/core/dev";import{z as n}from"@walkeros/core/dev";var o=n.object({bucket:n.string().min(1).describe("S3 bucket name"),endpoint:n.string().url().describe("S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)"),accessKeyId:n.string().min(1).describe("S3 access key ID"),secretAccessKey:n.string().min(1).describe("S3 secret access key"),region:n.string().default("auto").describe("AWS region for SigV4 signing"),prefix:n.string().optional().describe("Key prefix prepended to all store keys for scoping")});import{z as i}from"@walkeros/core/dev";var a=i.object({region:i.string().default("eu-central-1").describe('Region the bucket is created in. Defaults to settings.region when concrete (not "auto"), otherwise eu-central-1.')}).describe('Provisioning options for "walkeros setup store.<id>". Idempotent: only the bucket is created. Encryption, public-access block, versioning, lifecycle rules, and tags are not applied here (s3mini does not expose those operations); configure them once via the AWS Console or "aws s3api".'),c=r(o),u=r(a),p={};t(p,{awsAssets:()=>y,env:()=>l,r2Bucket:()=>S,step:()=>m,structuredKv:()=>b});var l={};function d(){const e=new Map;return async(t,s)=>{const{pathname:r}=new URL(function(e){return"string"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),n=s?.method??"GET";if(r.replace(/\/+$/,"").split("/").length<=2)return new Response(null,{status:200});if("PUT"===n)return e.set(r,(o=s?.body,"string"==typeof o?(new TextEncoder).encode(o):o instanceof Uint8Array?new Uint8Array(o):new Uint8Array)),new Response(null,{status:200,headers:{etag:'"mock"'}});var o;if("DELETE"===n)return e.delete(r),new Response(null,{status:204});const i=e.get(r);return i?new Response(i,{status:200}):new Response(null,{status:404})}}t(l,{createS3Fetch:()=>d,push:()=>g,simulation:()=>f});var g={get fetch(){return d()}},f=["fetch"],m={};t(m,{prefixScoping:()=>w,readAwsS3:()=>h});var h={title:"Read from S3",description:"Read object from S3 and receive its raw bytes byte-exact",in:{operation:"get",key:"walker.js"},out:[["get","walker.js","Bytes<(function(){...})()>"]]},w={title:"Prefix scoping",description:'Key "walker.js" with prefix "public/" resolves to S3 path "public/walker.js"',in:{operation:"get",key:"walker.js",settings:{bucket:"my-assets",prefix:"public"}},out:[["get","public/walker.js","Bytes<...>"]]},y={settings:{bucket:"my-assets",endpoint:"https://s3.eu-west-1.amazonaws.com",accessKeyId:"$env.S3_ACCESS_KEY",secretAccessKey:"$env.S3_SECRET_KEY",region:"eu-west-1",prefix:"public"},file:!0},b={settings:{bucket:"my-state",endpoint:"https://s3.eu-west-1.amazonaws.com",accessKeyId:"$env.S3_ACCESS_KEY",secretAccessKey:"$env.S3_SECRET_KEY",region:"eu-west-1"}},S={settings:{bucket:"my-bucket",endpoint:"https://ACCOUNT_ID.r2.cloudflarestorage.com",accessKeyId:"$env.R2_ACCESS_KEY",secretAccessKey:"$env.R2_SECRET_KEY"},file:!0},k={"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{p as examples,k as hints,s 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, type Setup } 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('S3 bucket name'),\n endpoint: z\n .string()\n .url()\n .describe(\n 'S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)',\n ),\n accessKeyId: z.string().min(1).describe('S3 access key ID'),\n secretAccessKey: z.string().min(1).describe('S3 secret access key'),\n region: z.string().default('auto').describe('AWS region for SigV4 signing'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n region: z\n .string()\n .default('eu-central-1')\n .describe(\n 'Region the bucket is created in. Defaults to settings.region when concrete (not \"auto\"), otherwise eu-central-1.',\n ),\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: only the bucket is created. Encryption, public-access block, versioning, lifecycle rules, and tags are not applied here (s3mini does not expose those operations); configure them once via the AWS Console or \"aws s3api\".',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\n/** AWS S3 bucket for serving static files byte-exact (file mode) */\nexport const awsAssets: Store.Config = {\n settings: {\n bucket: 'my-assets',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n prefix: 'public',\n },\n file: true,\n};\n\n/** S3 as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n },\n};\n\n/** Cloudflare R2 bucket, no egress fees */\nexport const r2Bucket: Store.Config = {\n settings: {\n bucket: 'my-bucket',\n endpoint: 'https://ACCOUNT_ID.r2.cloudflarestorage.com',\n accessKeyId: '$env.R2_ACCESS_KEY',\n secretAccessKey: '$env.R2_SECRET_KEY',\n },\n file: true,\n};\n\nexport * as step from './step';\n","import type { Flow } from '@walkeros/core';\n\n/** Read an object from an AWS S3 bucket (file mode, byte-exact). */\nexport const readAwsS3: Flow.StepExample = {\n title: 'Read from S3',\n description: 'Read object from S3 and receive its raw bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped to the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to S3 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,gBAAgB;AAAA,EACnD,UAAU,EACP,OAAO,EACP,IAAI,EACJ;AAAA,IACC;AAAA,EACF;AAAA,EACF,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,kBAAkB;AAAA,EAC1D,iBAAiB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,sBAAsB;AAAA,EAClE,QAAQ,EAAE,OAAO,EAAE,QAAQ,MAAM,EAAE,SAAS,8BAA8B;AAAA,EAC1E,QAAQ,EACL,OAAO,EACP,SAAS,EACT,SAAS,oDAAoD;AAClE,CAAC;;;ACjBD,SAAS,KAAAA,UAAS;AAEX,IAAM,cAAcA,GACxB,OAAO;AAAA,EACN,QAAQA,GACL,OAAO,EACP,QAAQ,cAAc,EACtB;AAAA,IACC;AAAA,EACF;AACJ,CAAC,EACA;AAAA,EACC;AACF;;;AFNK,IAAM,WAAW,YAAY,cAAc;AAC3C,IAAM,QAAQ,YAAY,WAAW;;;AGR5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,YAA8B;AAAA,EACzC,OAAO;AAAA,EACP,aAAa;AAAA,EACb,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;;;
|
|
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, type Setup } 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('S3 bucket name'),\n endpoint: z\n .string()\n .url()\n .describe(\n 'S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)',\n ),\n accessKeyId: z.string().min(1).describe('S3 access key ID'),\n secretAccessKey: z.string().min(1).describe('S3 secret access key'),\n region: z.string().default('auto').describe('AWS region for SigV4 signing'),\n prefix: z\n .string()\n .optional()\n .describe('Key prefix prepended to all store keys for scoping'),\n});\n\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\nexport const SetupSchema = z\n .object({\n region: z\n .string()\n .default('eu-central-1')\n .describe(\n 'Region the bucket is created in. Defaults to settings.region when concrete (not \"auto\"), otherwise eu-central-1.',\n ),\n })\n .describe(\n 'Provisioning options for \"walkeros setup store.<id>\". Idempotent: only the bucket is created. Encryption, public-access block, versioning, lifecycle rules, and tags are not applied here (s3mini does not expose those operations); configure them once via the AWS Console or \"aws s3api\".',\n );\n\nexport type Setup = z.infer<typeof SetupSchema>;\n","import type { Store } from '@walkeros/core';\n\nexport * as env from './env';\n\n/** AWS S3 bucket for serving static files byte-exact (file mode) */\nexport const awsAssets: Store.Config = {\n settings: {\n bucket: 'my-assets',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n prefix: 'public',\n },\n file: true,\n};\n\n/** S3 as a structured key-value store (default mode, stored as JSON) */\nexport const structuredKv: Store.Config = {\n settings: {\n bucket: 'my-state',\n endpoint: 'https://s3.eu-west-1.amazonaws.com',\n accessKeyId: '$env.S3_ACCESS_KEY',\n secretAccessKey: '$env.S3_SECRET_KEY',\n region: 'eu-west-1',\n },\n};\n\n/** Cloudflare R2 bucket, no egress fees */\nexport const r2Bucket: Store.Config = {\n settings: {\n bucket: 'my-bucket',\n endpoint: 'https://ACCOUNT_ID.r2.cloudflarestorage.com',\n accessKeyId: '$env.R2_ACCESS_KEY',\n secretAccessKey: '$env.R2_SECRET_KEY',\n },\n file: true,\n};\n\nexport * as step from './step';\n","import type { Env } from '../types';\n\n/**\n * Mock env for the S3 store: an in-memory bucket behind `fetch`.\n *\n * The store's S3 client (s3mini) signs each request and sends it through the\n * injected `fetch`. This mock answers the bucket existence check at start\n * and object get, put and delete. Objects live in a `Map` keyed by the\n * request path, so a `get` after a `set` returns what was written. Any\n * bucket exists; no credentials are checked.\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 S3 backend. */\nexport function createS3Fetch(): typeof fetch {\n const objects = new Map<string, Uint8Array<ArrayBuffer>>();\n\n return async (input, init) => {\n const { pathname } = new URL(requestUrl(input));\n const method = init?.method ?? 'GET';\n // Path style: /<bucket> for the bucket, /<bucket>/<key> for an object.\n const isBucket = pathname.replace(/\\/+$/, '').split('/').length <= 2;\n\n if (isBucket) return new Response(null, { status: 200 });\n\n if (method === 'PUT') {\n objects.set(pathname, bodyBytes(init?.body));\n return new Response(null, { status: 200, headers: { etag: '\"mock\"' } });\n }\n if (method === 'DELETE') {\n objects.delete(pathname);\n return new Response(null, { status: 204 });\n }\n\n const bytes = objects.get(pathname);\n if (!bytes) return new Response(null, { status: 404 });\n return new Response(bytes, { status: 200 });\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 createS3Fetch();\n },\n};\n\n/** Every request goes through `fetch`; `args[0]` is the signed URL. */\nexport const simulation = ['fetch'];\n","import type { Flow } from '@walkeros/core';\n\n/** Read an object from an AWS S3 bucket (file mode, byte-exact). */\nexport const readAwsS3: Flow.StepExample = {\n title: 'Read from S3',\n description: 'Read object from S3 and receive its raw bytes byte-exact',\n in: { operation: 'get', key: 'walker.js' },\n out: [['get', 'walker.js', 'Bytes<(function(){...})()>']],\n};\n\n/** Key is scoped to the configured prefix subdirectory. */\nexport const prefixScoping: Flow.StepExample = {\n title: 'Prefix scoping',\n description:\n 'Key \"walker.js\" with prefix \"public/\" resolves to S3 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
|
@@ -58,7 +58,14 @@ interface S3StoreSetup {
|
|
|
58
58
|
*/
|
|
59
59
|
region?: string;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Injected dependencies (tests, simulate). The S3 client sends every request
|
|
63
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
64
|
+
*/
|
|
65
|
+
interface Env extends Store.BaseEnv {
|
|
66
|
+
fetch?: typeof fetch;
|
|
67
|
+
}
|
|
68
|
+
type Types = Store.Types<S3StoreSettings, Env, S3StoreInitSettings, S3StoreSetup>;
|
|
62
69
|
|
|
63
70
|
/**
|
|
64
71
|
* Default setup options. EU region by default per AGENT.md.
|
|
@@ -101,4 +108,4 @@ interface StoreS3Module {
|
|
|
101
108
|
}
|
|
102
109
|
declare const storeS3: StoreS3Module;
|
|
103
110
|
|
|
104
|
-
export { DEFAULT_SETUP, type S3StoreConfig, type S3StoreInitSettings, type S3StoreSettings, type S3StoreSetup, type SetupResult, type StoreS3Module, type Types, storeS3 as default, setup, storeS3Init };
|
|
111
|
+
export { DEFAULT_SETUP, type Env, type S3StoreConfig, type S3StoreInitSettings, type S3StoreSettings, type S3StoreSetup, type SetupResult, type StoreS3Module, type Types, storeS3 as default, setup, storeS3Init };
|
package/dist/index.d.ts
CHANGED
|
@@ -58,7 +58,14 @@ interface S3StoreSetup {
|
|
|
58
58
|
*/
|
|
59
59
|
region?: string;
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Injected dependencies (tests, simulate). The S3 client sends every request
|
|
63
|
+
* through `fetch`; without it the global `fetch` is used.
|
|
64
|
+
*/
|
|
65
|
+
interface Env extends Store.BaseEnv {
|
|
66
|
+
fetch?: typeof fetch;
|
|
67
|
+
}
|
|
68
|
+
type Types = Store.Types<S3StoreSettings, Env, S3StoreInitSettings, S3StoreSetup>;
|
|
62
69
|
|
|
63
70
|
/**
|
|
64
71
|
* Default setup options. EU region by default per AGENT.md.
|
|
@@ -101,4 +108,4 @@ interface StoreS3Module {
|
|
|
101
108
|
}
|
|
102
109
|
declare const storeS3: StoreS3Module;
|
|
103
110
|
|
|
104
|
-
export { DEFAULT_SETUP, type S3StoreConfig, type S3StoreInitSettings, type S3StoreSettings, type S3StoreSetup, type SetupResult, type StoreS3Module, type Types, storeS3 as default, setup, storeS3Init };
|
|
111
|
+
export { DEFAULT_SETUP, type Env, type S3StoreConfig, type S3StoreInitSettings, type S3StoreSettings, type S3StoreSetup, type SetupResult, type StoreS3Module, type Types, storeS3 as default, setup, storeS3Init };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,s={};((e,r)=>{for(var n in r)t(e,n,{get:r[n],enumerable:!0})})(s,{DEFAULT_SETUP:()=>d,default:()=>w,setup:()=>
|
|
1
|
+
"use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,s={};((e,r)=>{for(var n in r)t(e,n,{get:r[n],enumerable:!0})})(s,{DEFAULT_SETUP:()=>d,default:()=>w,setup:()=>l,storeS3Init:()=>g}),module.exports=(e=s,((e,s,o,c)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let u of n(s))i.call(e,u)||u===o||t(e,u,{get:()=>s[u],enumerable:!(c=r(s,u))||c.enumerable});return e})(t({},"__esModule",{value:!0}),e));var o=require("s3mini"),c=require("@walkeros/core");var u="application/octet-stream",a={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 g=async e=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeS3Init: settings.bucket is required (non-empty string)");if("string"!=typeof e.endpoint||0===e.endpoint.length)throw new Error("storeS3Init: settings.endpoint is required (non-empty string)");if("string"!=typeof e.accessKeyId||0===e.accessKeyId.length)throw new Error("storeS3Init: settings.accessKeyId is required (non-empty string)");if("string"!=typeof e.secretAccessKey||0===e.secretAccessKey.length)throw new Error("storeS3Init: settings.secretAccessKey is required (non-empty string)")}(e.config.settings);const t=e.config.settings,r=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(t.prefix),n=!0===e.config.file,i=e.env?.fetch??e.config.env?.fetch,s=new o.S3mini({endpoint:(g=t.endpoint,f=t.bucket,`${g.replace(/\/+$/,"")}/${f}`),accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey,region:t.region||"auto",...i?{fetch:i}:{}});var g,f;function p(t){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(t))return r+t;e.logger.warn("Invalid key rejected",{key:t})}if(!await s.bucketExists()){const r=e.id??"s3",n=`S3 bucket not found: ${t.bucket} at ${t.endpoint}. Run "walkeros setup store.${r}" to create it.`;throw e.logger.error(n,{bucket:t.bucket,endpoint:t.endpoint}),new Error(n)}return{type:"s3",config:{settings:t,env:e.config.env,id:e.config.id,logger:e.config.logger,file:e.config.file},async get(t){const r=p(t);if(!r)return;let i;try{const e=await s.getObjectArrayBuffer(r);if(!e)return;i=Buffer.from(e)}catch{return}if(n)return i;try{return(0,c.deserializeStoreValue)(i)}catch(r){return void e.logger.debug("structured decode failed, degrading to miss",{key:t,error:r instanceof Error?r.message:String(r)})}},async set(e,t){const r=p(e);if(r)if(n){if(!(t instanceof Uint8Array)&&"string"!=typeof t)throw new Error("storeS3Init.set: in file mode value must be Uint8Array or string; got "+typeof t);await s.putObject(r,t,function(e){const t=e.lastIndexOf(".");if(t<0||t===e.length-1)return u;const r=e.slice(t+1).toLowerCase();return a[r]??u}(e))}else await s.putObject(r,(0,c.serializeStoreValue)(t),"application/json")},async delete(e){const t=p(e);if(t)try{await s.deleteObject(t)}catch{}}}},f=require("s3mini"),p=require("@walkeros/core"),d={region:"eu-central-1"},l=async e=>{const{config:t,logger:r}=e,n=(0,p.resolveSetup)(t.setup,d);if(!n)return void r.debug("setup: skipped (config.setup is false or unset)");!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("setup: settings.bucket is required (non-empty string)");if("string"!=typeof e.endpoint||0===e.endpoint.length)throw new Error("setup: settings.endpoint is required (non-empty string)");if("string"!=typeof e.accessKeyId||0===e.accessKeyId.length)throw new Error("setup: settings.accessKeyId is required (non-empty string)");if("string"!=typeof e.secretAccessKey||0===e.secretAccessKey.length)throw new Error("setup: settings.secretAccessKey is required (non-empty string)")}(t.settings);const i=t.settings,s=function(e,t){if("string"==typeof e&&e.length>0)return e;if("string"==typeof t&&t.length>0&&"auto"!==t)return t;return d.region}(function(e){if(!function(e){return"object"==typeof e&&null!==e}(e))return;const t=e.region;return"string"==typeof t?t:void 0}(t.setup),i.region),o=new f.S3mini({endpoint:y(i.endpoint,i.bucket),accessKeyId:i.accessKeyId,secretAccessKey:i.secretAccessKey,region:s});if(await o.bucketExists())return r.debug("setup: bucket exists",{bucket:i.bucket}),{bucketCreated:!1};try{return await o.createBucket(),r.info("setup: bucket created",{bucket:i.bucket,region:s}),{bucketCreated:!0}}catch(e){if(function(e){return b(e,"BucketAlreadyOwnedByYou")}(e))return r.debug("setup: bucket created concurrently (race)",{bucket:i.bucket}),{bucketCreated:!1};if(function(e){return b(e,"BucketAlreadyExists")}(e))throw new Error(`S3 bucket name "${i.bucket}" is already in use by another AWS account. Choose a different bucket name in settings.bucket.`);throw e}};function y(e,t){return`${e.replace(/\/+$/,"")}/${t}`}function b(e,t){if("object"!=typeof e||null===e)return!1;const r=e;return r.code===t||r.Code===t||r.name===t||r.serviceCode===t}var w={type:"s3",init:g,setup:l};//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/setup.ts"],"sourcesContent":["import type { Store, SetupFn } from '@walkeros/core';\nimport { storeS3Init } from './store';\nimport { setup } from './setup';\nimport type { Types } from './types';\nimport type { S3StoreConfig } from './setup';\n\nexport { storeS3Init } from './store';\nexport { setup, DEFAULT_SETUP } from './setup';\nexport type { SetupResult, S3StoreConfig } from './setup';\nexport type {\n S3StoreSettings,\n S3StoreInitSettings,\n S3StoreSetup,\n Types,\n} from './types';\n\n/**\n * Default export shape for store packages. The CLI (`walkeros setup store.<id>`)\n * reads `default.setup` to find the lifecycle function; the collector reads\n * `default.init` (or the named export) to construct the runtime instance.\n *\n * Soft breaking change: callers using `import storeS3Init from '@walkeros/server-store-s3'`\n * (default import) must now call `.init` on the returned object. Named imports\n * (`import { storeS3Init }`) are unaffected.\n */\nexport interface StoreS3Module {\n type: 's3';\n init: Store.Init<Types>;\n setup: SetupFn<S3StoreConfig, Store.BaseEnv>;\n}\n\nconst storeS3: StoreS3Module = {\n type: 's3',\n init: storeS3Init,\n setup,\n};\n\nexport default storeS3;\n","import { S3mini } from 's3mini';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type { Store } from '@walkeros/core';\nimport type { S3StoreSettings, Types } from './types';\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\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\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction assertSettings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.bucket is required (non-empty string)',\n );\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error(\n 'storeS3Init: settings.endpoint is required (non-empty string)',\n );\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nexport const storeS3Init: Store.Init<Types> = async (context) => {\n assertSettings(context.config.settings);\n const settings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\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 const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region: settings.region || 'auto',\n });\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n context.logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n // Hard-fail with an actionable message if the bucket is missing.\n // Mirrors the BigQuery init-time hard-fail pattern.\n const exists = await client.bucketExists();\n if (!exists) {\n const setupId = context.id ?? 's3';\n const errorMsg =\n `S3 bucket not found: ${settings.bucket} at ${settings.endpoint}. ` +\n `Run \"walkeros setup store.${setupId}\" to create it.`;\n context.logger.error(errorMsg, {\n bucket: settings.bucket,\n endpoint: settings.endpoint,\n });\n throw new Error(errorMsg);\n }\n\n return {\n type: 's3',\n config: {\n settings,\n env: context.config.env,\n id: context.config.id,\n logger: context.config.logger,\n },\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const s3Key = resolveKey(key);\n if (!s3Key) return undefined;\n\n let bytes: Buffer;\n try {\n const arrayBuffer = await client.getObjectArrayBuffer(s3Key);\n if (!arrayBuffer) return undefined;\n bytes = Buffer.from(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 (e.g. a 0-byte object) degrades to a miss\n // rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n context.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 s3Key = resolveKey(key);\n if (!s3Key) return;\n\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (!(value instanceof Uint8Array) && typeof value !== 'string') {\n throw new Error(\n 'storeS3Init.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n await client.putObject(s3Key, value, mimeFromKey(key));\n return;\n }\n\n // Structured mode: serialize to utf8-JSON bytes, stored as JSON.\n await client.putObject(\n s3Key,\n serializeStoreValue(value),\n JSON_CONTENT_TYPE,\n );\n },\n\n async delete(key: string): Promise<void> {\n const s3Key = resolveKey(key);\n if (!s3Key) return;\n\n try {\n await client.deleteObject(s3Key);\n } catch {\n /* S3 delete is idempotent */\n }\n },\n };\n};\n","import { S3mini } from 's3mini';\nimport type { LifecycleContext, SetupFn, Store } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { S3StoreSettings, S3StoreSetup, Types } from './types';\n\n/**\n * Default setup options. EU region by default per AGENT.md.\n */\nexport const DEFAULT_SETUP: Required<S3StoreSetup> = {\n region: 'eu-central-1',\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 S3StoreConfig = Store.Config<Types>;\n\n/**\n * Provision the S3 bucket described in the flow config. Idempotent. Triggered\n * only by the explicit `walkeros setup store.<id>` CLI command.\n *\n * Variant B: only the bucket is created. Encryption, public-access block,\n * versioning, lifecycle rules, and tags are not applied here because `s3mini`\n * does not expose the relevant API operations. Configure those once via the\n * AWS Console or `aws s3api`.\n */\nexport const setup: SetupFn<S3StoreConfig, Store.BaseEnv> = async (\n context: LifecycleContext<S3StoreConfig, Store.BaseEnv>,\n) => {\n const { config, logger } = context;\n\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 assertS3Settings(config.settings);\n const settings = config.settings;\n\n // Region resolution per Open Question #4 (iii):\n // - explicit setup.region wins\n // - else fall back to settings.region when concrete (not 'auto')\n // - else default eu-central-1\n const explicitSetupRegion = readExplicitRegion(config.setup);\n const region = resolveRegion(explicitSetupRegion, settings.region);\n void options; // resolveSetup applies defaults; explicit branch handled separately\n\n const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region,\n });\n\n const exists = await client.bucketExists();\n if (exists) {\n logger.debug('setup: bucket exists', { bucket: settings.bucket });\n return { bucketCreated: false };\n }\n\n try {\n await client.createBucket();\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n region,\n });\n return { bucketCreated: true };\n } catch (err: unknown) {\n if (isAlreadyOwnedByYou(err)) {\n logger.debug('setup: bucket created concurrently (race)', {\n bucket: settings.bucket,\n });\n return { bucketCreated: false };\n }\n if (isAlreadyExistsDifferentOwner(err)) {\n throw new Error(\n `S3 bucket name \"${settings.bucket}\" is already in use by another AWS account. ` +\n `Choose a different bucket name in settings.bucket.`,\n );\n }\n throw err;\n }\n};\n\nfunction resolveRegion(\n explicitSetupRegion: string | undefined,\n settingsRegion: string | undefined,\n): string {\n if (\n typeof explicitSetupRegion === 'string' &&\n explicitSetupRegion.length > 0\n ) {\n return explicitSetupRegion;\n }\n if (\n typeof settingsRegion === 'string' &&\n settingsRegion.length > 0 &&\n settingsRegion !== 'auto'\n ) {\n return settingsRegion;\n }\n return DEFAULT_SETUP.region;\n}\n\nfunction readExplicitRegion(raw: S3StoreConfig['setup']): string | undefined {\n if (!isRecord(raw)) return undefined;\n const value = raw.region;\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction assertS3Settings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error('setup: settings.bucket is required (non-empty string)');\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error('setup: settings.endpoint is required (non-empty string)');\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'setup: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'setup: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction isAlreadyOwnedByYou(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyOwnedByYou');\n}\n\nfunction isAlreadyExistsDifferentOwner(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyExists');\n}\n\nfunction hasErrorCode(err: unknown, code: string): boolean {\n if (typeof err !== 'object' || err === null) return false;\n const obj: {\n code?: unknown;\n Code?: unknown;\n name?: unknown;\n serviceCode?: unknown;\n } = err;\n return (\n obj.code === code ||\n obj.Code === code ||\n obj.name === code ||\n obj.serviceCode === code\n );\n}\n"],"mappings":"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","names":["import_s3mini","import_core","buildEndpoint"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/store.ts","../src/setup.ts"],"sourcesContent":["import type { Store, SetupFn } from '@walkeros/core';\nimport { storeS3Init } from './store';\nimport { setup } from './setup';\nimport type { Types } from './types';\nimport type { S3StoreConfig } from './setup';\n\nexport { storeS3Init } from './store';\nexport { setup, DEFAULT_SETUP } from './setup';\nexport type { SetupResult, S3StoreConfig } from './setup';\nexport type {\n Env,\n S3StoreSettings,\n S3StoreInitSettings,\n S3StoreSetup,\n Types,\n} from './types';\n\n/**\n * Default export shape for store packages. The CLI (`walkeros setup store.<id>`)\n * reads `default.setup` to find the lifecycle function; the collector reads\n * `default.init` (or the named export) to construct the runtime instance.\n *\n * Soft breaking change: callers using `import storeS3Init from '@walkeros/server-store-s3'`\n * (default import) must now call `.init` on the returned object. Named imports\n * (`import { storeS3Init }`) are unaffected.\n */\nexport interface StoreS3Module {\n type: 's3';\n init: Store.Init<Types>;\n setup: SetupFn<S3StoreConfig, Store.BaseEnv>;\n}\n\nconst storeS3: StoreS3Module = {\n type: 's3',\n init: storeS3Init,\n setup,\n};\n\nexport default storeS3;\n","import { S3mini } from 's3mini';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type { Store } from '@walkeros/core';\nimport type { S3StoreSettings, Types } from './types';\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\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\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction assertSettings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.bucket is required (non-empty string)',\n );\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error(\n 'storeS3Init: settings.endpoint is required (non-empty string)',\n );\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nexport const storeS3Init: Store.Init<Types> = async (context) => {\n assertSettings(context.config.settings);\n const settings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\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 // An injected fetch (tests, simulate) carries every S3 request; s3mini\n // uses the global fetch when none is given.\n const injectedFetch = context.env?.fetch ?? context.config.env?.fetch;\n const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region: settings.region || 'auto',\n ...(injectedFetch ? { fetch: injectedFetch } : {}),\n });\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n context.logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n // Hard-fail with an actionable message if the bucket is missing.\n // Mirrors the BigQuery init-time hard-fail pattern.\n const exists = await client.bucketExists();\n if (!exists) {\n const setupId = context.id ?? 's3';\n const errorMsg =\n `S3 bucket not found: ${settings.bucket} at ${settings.endpoint}. ` +\n `Run \"walkeros setup store.${setupId}\" to create it.`;\n context.logger.error(errorMsg, {\n bucket: settings.bucket,\n endpoint: settings.endpoint,\n });\n throw new Error(errorMsg);\n }\n\n return {\n type: 's3',\n config: {\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 },\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const s3Key = resolveKey(key);\n if (!s3Key) return undefined;\n\n let bytes: Buffer;\n try {\n const arrayBuffer = await client.getObjectArrayBuffer(s3Key);\n if (!arrayBuffer) return undefined;\n bytes = Buffer.from(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 (e.g. a 0-byte object) degrades to a miss\n // rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n context.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 s3Key = resolveKey(key);\n if (!s3Key) return;\n\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (!(value instanceof Uint8Array) && typeof value !== 'string') {\n throw new Error(\n 'storeS3Init.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n await client.putObject(s3Key, value, mimeFromKey(key));\n return;\n }\n\n // Structured mode: serialize to utf8-JSON bytes, stored as JSON.\n await client.putObject(\n s3Key,\n serializeStoreValue(value),\n JSON_CONTENT_TYPE,\n );\n },\n\n async delete(key: string): Promise<void> {\n const s3Key = resolveKey(key);\n if (!s3Key) return;\n\n try {\n await client.deleteObject(s3Key);\n } catch {\n /* S3 delete is idempotent */\n }\n },\n };\n};\n","import { S3mini } from 's3mini';\nimport type { LifecycleContext, SetupFn, Store } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { S3StoreSettings, S3StoreSetup, Types } from './types';\n\n/**\n * Default setup options. EU region by default per AGENT.md.\n */\nexport const DEFAULT_SETUP: Required<S3StoreSetup> = {\n region: 'eu-central-1',\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 S3StoreConfig = Store.Config<Types>;\n\n/**\n * Provision the S3 bucket described in the flow config. Idempotent. Triggered\n * only by the explicit `walkeros setup store.<id>` CLI command.\n *\n * Variant B: only the bucket is created. Encryption, public-access block,\n * versioning, lifecycle rules, and tags are not applied here because `s3mini`\n * does not expose the relevant API operations. Configure those once via the\n * AWS Console or `aws s3api`.\n */\nexport const setup: SetupFn<S3StoreConfig, Store.BaseEnv> = async (\n context: LifecycleContext<S3StoreConfig, Store.BaseEnv>,\n) => {\n const { config, logger } = context;\n\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 assertS3Settings(config.settings);\n const settings = config.settings;\n\n // Region resolution per Open Question #4 (iii):\n // - explicit setup.region wins\n // - else fall back to settings.region when concrete (not 'auto')\n // - else default eu-central-1\n const explicitSetupRegion = readExplicitRegion(config.setup);\n const region = resolveRegion(explicitSetupRegion, settings.region);\n void options; // resolveSetup applies defaults; explicit branch handled separately\n\n const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region,\n });\n\n const exists = await client.bucketExists();\n if (exists) {\n logger.debug('setup: bucket exists', { bucket: settings.bucket });\n return { bucketCreated: false };\n }\n\n try {\n await client.createBucket();\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n region,\n });\n return { bucketCreated: true };\n } catch (err: unknown) {\n if (isAlreadyOwnedByYou(err)) {\n logger.debug('setup: bucket created concurrently (race)', {\n bucket: settings.bucket,\n });\n return { bucketCreated: false };\n }\n if (isAlreadyExistsDifferentOwner(err)) {\n throw new Error(\n `S3 bucket name \"${settings.bucket}\" is already in use by another AWS account. ` +\n `Choose a different bucket name in settings.bucket.`,\n );\n }\n throw err;\n }\n};\n\nfunction resolveRegion(\n explicitSetupRegion: string | undefined,\n settingsRegion: string | undefined,\n): string {\n if (\n typeof explicitSetupRegion === 'string' &&\n explicitSetupRegion.length > 0\n ) {\n return explicitSetupRegion;\n }\n if (\n typeof settingsRegion === 'string' &&\n settingsRegion.length > 0 &&\n settingsRegion !== 'auto'\n ) {\n return settingsRegion;\n }\n return DEFAULT_SETUP.region;\n}\n\nfunction readExplicitRegion(raw: S3StoreConfig['setup']): string | undefined {\n if (!isRecord(raw)) return undefined;\n const value = raw.region;\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction assertS3Settings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error('setup: settings.bucket is required (non-empty string)');\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error('setup: settings.endpoint is required (non-empty string)');\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'setup: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'setup: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction isAlreadyOwnedByYou(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyOwnedByYou');\n}\n\nfunction isAlreadyExistsDifferentOwner(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyExists');\n}\n\nfunction hasErrorCode(err: unknown, code: string): boolean {\n if (typeof err !== 'object' || err === null) return false;\n const obj: {\n code?: unknown;\n Code?: unknown;\n name?: unknown;\n serviceCode?: unknown;\n } = err;\n return (\n obj.code === code ||\n obj.Code === code ||\n obj.name === code ||\n obj.serviceCode === code\n );\n}\n"],"mappings":"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","names":["import_s3mini","import_core","buildEndpoint"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{S3mini as e}from"s3mini";import{serializeStoreValue as t,deserializeStoreValue as n}from"@walkeros/core";var r="application/octet-stream",i={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 s=async s=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeS3Init: settings.bucket is required (non-empty string)");if("string"!=typeof e.endpoint||0===e.endpoint.length)throw new Error("storeS3Init: settings.endpoint is required (non-empty string)");if("string"!=typeof e.accessKeyId||0===e.accessKeyId.length)throw new Error("storeS3Init: settings.accessKeyId is required (non-empty string)");if("string"!=typeof e.secretAccessKey||0===e.secretAccessKey.length)throw new Error("storeS3Init: settings.secretAccessKey is required (non-empty string)")}(s.config.settings);const
|
|
1
|
+
import{S3mini as e}from"s3mini";import{serializeStoreValue as t,deserializeStoreValue as n}from"@walkeros/core";var r="application/octet-stream",i={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 s=async s=>{!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("storeS3Init: settings.bucket is required (non-empty string)");if("string"!=typeof e.endpoint||0===e.endpoint.length)throw new Error("storeS3Init: settings.endpoint is required (non-empty string)");if("string"!=typeof e.accessKeyId||0===e.accessKeyId.length)throw new Error("storeS3Init: settings.accessKeyId is required (non-empty string)");if("string"!=typeof e.secretAccessKey||0===e.secretAccessKey.length)throw new Error("storeS3Init: settings.secretAccessKey is required (non-empty string)")}(s.config.settings);const c=s.config.settings,o=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(c.prefix),u=!0===s.config.file,a=s.env?.fetch??s.config.env?.fetch,g=new e({endpoint:(f=c.endpoint,p=c.bucket,`${f.replace(/\/+$/,"")}/${p}`),accessKeyId:c.accessKeyId,secretAccessKey:c.secretAccessKey,region:c.region||"auto",...a?{fetch:a}:{}});var f,p;function d(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return o+e;s.logger.warn("Invalid key rejected",{key:e})}if(!await g.bucketExists()){const e=s.id??"s3",t=`S3 bucket not found: ${c.bucket} at ${c.endpoint}. Run "walkeros setup store.${e}" to create it.`;throw s.logger.error(t,{bucket:c.bucket,endpoint:c.endpoint}),new Error(t)}return{type:"s3",config:{settings:c,env:s.config.env,id:s.config.id,logger:s.config.logger,file:s.config.file},async get(e){const t=d(e);if(!t)return;let r;try{const e=await g.getObjectArrayBuffer(t);if(!e)return;r=Buffer.from(e)}catch{return}if(u)return r;try{return n(r)}catch(t){return void s.logger.debug("structured decode failed, degrading to miss",{key:e,error:t instanceof Error?t.message:String(t)})}},async set(e,n){const s=d(e);if(s)if(u){if(!(n instanceof Uint8Array)&&"string"!=typeof n)throw new Error("storeS3Init.set: in file mode value must be Uint8Array or string; got "+typeof n);await g.putObject(s,n,function(e){const t=e.lastIndexOf(".");if(t<0||t===e.length-1)return r;const n=e.slice(t+1).toLowerCase();return i[n]??r}(e))}else await g.putObject(s,t(n),"application/json")},async delete(e){const t=d(e);if(t)try{await g.deleteObject(t)}catch{}}}};import{S3mini as c}from"s3mini";import{resolveSetup as o}from"@walkeros/core";var u={region:"eu-central-1"},a=async e=>{const{config:t,logger:n}=e,r=o(t.setup,u);if(!r)return void n.debug("setup: skipped (config.setup is false or unset)");!function(e){if(!e||"string"!=typeof e.bucket||0===e.bucket.length)throw new Error("setup: settings.bucket is required (non-empty string)");if("string"!=typeof e.endpoint||0===e.endpoint.length)throw new Error("setup: settings.endpoint is required (non-empty string)");if("string"!=typeof e.accessKeyId||0===e.accessKeyId.length)throw new Error("setup: settings.accessKeyId is required (non-empty string)");if("string"!=typeof e.secretAccessKey||0===e.secretAccessKey.length)throw new Error("setup: settings.secretAccessKey is required (non-empty string)")}(t.settings);const i=t.settings,s=function(e,t){if("string"==typeof e&&e.length>0)return e;if("string"==typeof t&&t.length>0&&"auto"!==t)return t;return u.region}(function(e){if(!function(e){return"object"==typeof e&&null!==e}(e))return;const t=e.region;return"string"==typeof t?t:void 0}(t.setup),i.region),a=new c({endpoint:g(i.endpoint,i.bucket),accessKeyId:i.accessKeyId,secretAccessKey:i.secretAccessKey,region:s});if(await a.bucketExists())return n.debug("setup: bucket exists",{bucket:i.bucket}),{bucketCreated:!1};try{return await a.createBucket(),n.info("setup: bucket created",{bucket:i.bucket,region:s}),{bucketCreated:!0}}catch(e){if(function(e){return f(e,"BucketAlreadyOwnedByYou")}(e))return n.debug("setup: bucket created concurrently (race)",{bucket:i.bucket}),{bucketCreated:!1};if(function(e){return f(e,"BucketAlreadyExists")}(e))throw new Error(`S3 bucket name "${i.bucket}" is already in use by another AWS account. Choose a different bucket name in settings.bucket.`);throw e}};function g(e,t){return`${e.replace(/\/+$/,"")}/${t}`}function f(e,t){if("object"!=typeof e||null===e)return!1;const n=e;return n.code===t||n.Code===t||n.name===t||n.serviceCode===t}var p={type:"s3",init:s,setup:a};export{u as DEFAULT_SETUP,p as default,a as setup,s as storeS3Init};//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/setup.ts","../src/index.ts"],"sourcesContent":["import { S3mini } from 's3mini';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type { Store } from '@walkeros/core';\nimport type { S3StoreSettings, Types } from './types';\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\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\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction assertSettings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.bucket is required (non-empty string)',\n );\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error(\n 'storeS3Init: settings.endpoint is required (non-empty string)',\n );\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nexport const storeS3Init: Store.Init<Types> = async (context) => {\n assertSettings(context.config.settings);\n const settings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\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 const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region: settings.region || 'auto',\n });\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n context.logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n // Hard-fail with an actionable message if the bucket is missing.\n // Mirrors the BigQuery init-time hard-fail pattern.\n const exists = await client.bucketExists();\n if (!exists) {\n const setupId = context.id ?? 's3';\n const errorMsg =\n `S3 bucket not found: ${settings.bucket} at ${settings.endpoint}. ` +\n `Run \"walkeros setup store.${setupId}\" to create it.`;\n context.logger.error(errorMsg, {\n bucket: settings.bucket,\n endpoint: settings.endpoint,\n });\n throw new Error(errorMsg);\n }\n\n return {\n type: 's3',\n config: {\n settings,\n env: context.config.env,\n id: context.config.id,\n logger: context.config.logger,\n },\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const s3Key = resolveKey(key);\n if (!s3Key) return undefined;\n\n let bytes: Buffer;\n try {\n const arrayBuffer = await client.getObjectArrayBuffer(s3Key);\n if (!arrayBuffer) return undefined;\n bytes = Buffer.from(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 (e.g. a 0-byte object) degrades to a miss\n // rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n context.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 s3Key = resolveKey(key);\n if (!s3Key) return;\n\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (!(value instanceof Uint8Array) && typeof value !== 'string') {\n throw new Error(\n 'storeS3Init.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n await client.putObject(s3Key, value, mimeFromKey(key));\n return;\n }\n\n // Structured mode: serialize to utf8-JSON bytes, stored as JSON.\n await client.putObject(\n s3Key,\n serializeStoreValue(value),\n JSON_CONTENT_TYPE,\n );\n },\n\n async delete(key: string): Promise<void> {\n const s3Key = resolveKey(key);\n if (!s3Key) return;\n\n try {\n await client.deleteObject(s3Key);\n } catch {\n /* S3 delete is idempotent */\n }\n },\n };\n};\n","import { S3mini } from 's3mini';\nimport type { LifecycleContext, SetupFn, Store } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { S3StoreSettings, S3StoreSetup, Types } from './types';\n\n/**\n * Default setup options. EU region by default per AGENT.md.\n */\nexport const DEFAULT_SETUP: Required<S3StoreSetup> = {\n region: 'eu-central-1',\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 S3StoreConfig = Store.Config<Types>;\n\n/**\n * Provision the S3 bucket described in the flow config. Idempotent. Triggered\n * only by the explicit `walkeros setup store.<id>` CLI command.\n *\n * Variant B: only the bucket is created. Encryption, public-access block,\n * versioning, lifecycle rules, and tags are not applied here because `s3mini`\n * does not expose the relevant API operations. Configure those once via the\n * AWS Console or `aws s3api`.\n */\nexport const setup: SetupFn<S3StoreConfig, Store.BaseEnv> = async (\n context: LifecycleContext<S3StoreConfig, Store.BaseEnv>,\n) => {\n const { config, logger } = context;\n\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 assertS3Settings(config.settings);\n const settings = config.settings;\n\n // Region resolution per Open Question #4 (iii):\n // - explicit setup.region wins\n // - else fall back to settings.region when concrete (not 'auto')\n // - else default eu-central-1\n const explicitSetupRegion = readExplicitRegion(config.setup);\n const region = resolveRegion(explicitSetupRegion, settings.region);\n void options; // resolveSetup applies defaults; explicit branch handled separately\n\n const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region,\n });\n\n const exists = await client.bucketExists();\n if (exists) {\n logger.debug('setup: bucket exists', { bucket: settings.bucket });\n return { bucketCreated: false };\n }\n\n try {\n await client.createBucket();\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n region,\n });\n return { bucketCreated: true };\n } catch (err: unknown) {\n if (isAlreadyOwnedByYou(err)) {\n logger.debug('setup: bucket created concurrently (race)', {\n bucket: settings.bucket,\n });\n return { bucketCreated: false };\n }\n if (isAlreadyExistsDifferentOwner(err)) {\n throw new Error(\n `S3 bucket name \"${settings.bucket}\" is already in use by another AWS account. ` +\n `Choose a different bucket name in settings.bucket.`,\n );\n }\n throw err;\n }\n};\n\nfunction resolveRegion(\n explicitSetupRegion: string | undefined,\n settingsRegion: string | undefined,\n): string {\n if (\n typeof explicitSetupRegion === 'string' &&\n explicitSetupRegion.length > 0\n ) {\n return explicitSetupRegion;\n }\n if (\n typeof settingsRegion === 'string' &&\n settingsRegion.length > 0 &&\n settingsRegion !== 'auto'\n ) {\n return settingsRegion;\n }\n return DEFAULT_SETUP.region;\n}\n\nfunction readExplicitRegion(raw: S3StoreConfig['setup']): string | undefined {\n if (!isRecord(raw)) return undefined;\n const value = raw.region;\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction assertS3Settings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error('setup: settings.bucket is required (non-empty string)');\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error('setup: settings.endpoint is required (non-empty string)');\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'setup: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'setup: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction isAlreadyOwnedByYou(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyOwnedByYou');\n}\n\nfunction isAlreadyExistsDifferentOwner(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyExists');\n}\n\nfunction hasErrorCode(err: unknown, code: string): boolean {\n if (typeof err !== 'object' || err === null) return false;\n const obj: {\n code?: unknown;\n Code?: unknown;\n name?: unknown;\n serviceCode?: unknown;\n } = err;\n return (\n obj.code === code ||\n obj.Code === code ||\n obj.name === code ||\n obj.serviceCode === code\n );\n}\n","import type { Store, SetupFn } from '@walkeros/core';\nimport { storeS3Init } from './store';\nimport { setup } from './setup';\nimport type { Types } from './types';\nimport type { S3StoreConfig } from './setup';\n\nexport { storeS3Init } from './store';\nexport { setup, DEFAULT_SETUP } from './setup';\nexport type { SetupResult, S3StoreConfig } from './setup';\nexport type {\n S3StoreSettings,\n S3StoreInitSettings,\n S3StoreSetup,\n Types,\n} from './types';\n\n/**\n * Default export shape for store packages. The CLI (`walkeros setup store.<id>`)\n * reads `default.setup` to find the lifecycle function; the collector reads\n * `default.init` (or the named export) to construct the runtime instance.\n *\n * Soft breaking change: callers using `import storeS3Init from '@walkeros/server-store-s3'`\n * (default import) must now call `.init` on the returned object. Named imports\n * (`import { storeS3Init }`) are unaffected.\n */\nexport interface StoreS3Module {\n type: 's3';\n init: Store.Init<Types>;\n setup: SetupFn<S3StoreConfig, Store.BaseEnv>;\n}\n\nconst storeS3: StoreS3Module = {\n type: 's3',\n init: storeS3Init,\n setup,\n};\n\nexport default storeS3;\n"],"mappings":"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","names":["S3mini","buildEndpoint"]}
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/setup.ts","../src/index.ts"],"sourcesContent":["import { S3mini } from 's3mini';\nimport { serializeStoreValue, deserializeStoreValue } from '@walkeros/core';\nimport type { Store } from '@walkeros/core';\nimport type { S3StoreSettings, Types } from './types';\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\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\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction assertSettings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.bucket is required (non-empty string)',\n );\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error(\n 'storeS3Init: settings.endpoint is required (non-empty string)',\n );\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'storeS3Init: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nexport const storeS3Init: Store.Init<Types> = async (context) => {\n assertSettings(context.config.settings);\n const settings = context.config.settings;\n const prefix = normalizePrefix(settings.prefix);\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 // An injected fetch (tests, simulate) carries every S3 request; s3mini\n // uses the global fetch when none is given.\n const injectedFetch = context.env?.fetch ?? context.config.env?.fetch;\n const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region: settings.region || 'auto',\n ...(injectedFetch ? { fetch: injectedFetch } : {}),\n });\n\n function resolveKey(key: string): string | undefined {\n if (!isValidKey(key)) {\n context.logger.warn('Invalid key rejected', { key });\n return undefined;\n }\n return prefix + key;\n }\n\n // Hard-fail with an actionable message if the bucket is missing.\n // Mirrors the BigQuery init-time hard-fail pattern.\n const exists = await client.bucketExists();\n if (!exists) {\n const setupId = context.id ?? 's3';\n const errorMsg =\n `S3 bucket not found: ${settings.bucket} at ${settings.endpoint}. ` +\n `Run \"walkeros setup store.${setupId}\" to create it.`;\n context.logger.error(errorMsg, {\n bucket: settings.bucket,\n endpoint: settings.endpoint,\n });\n throw new Error(errorMsg);\n }\n\n return {\n type: 's3',\n config: {\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 },\n\n async get(key: string): Promise<Store.StoreValue | undefined> {\n const s3Key = resolveKey(key);\n if (!s3Key) return undefined;\n\n let bytes: Buffer;\n try {\n const arrayBuffer = await client.getObjectArrayBuffer(s3Key);\n if (!arrayBuffer) return undefined;\n bytes = Buffer.from(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 (e.g. a 0-byte object) degrades to a miss\n // rather than throwing a raw SyntaxError.\n if (fileMode) return bytes;\n try {\n return deserializeStoreValue(bytes);\n } catch (err) {\n context.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 s3Key = resolveKey(key);\n if (!s3Key) return;\n\n if (fileMode) {\n // Byte-exact passthrough: accept Uint8Array (Buffer included) or string.\n if (!(value instanceof Uint8Array) && typeof value !== 'string') {\n throw new Error(\n 'storeS3Init.set: in file mode value must be Uint8Array or string; got ' +\n typeof value,\n );\n }\n await client.putObject(s3Key, value, mimeFromKey(key));\n return;\n }\n\n // Structured mode: serialize to utf8-JSON bytes, stored as JSON.\n await client.putObject(\n s3Key,\n serializeStoreValue(value),\n JSON_CONTENT_TYPE,\n );\n },\n\n async delete(key: string): Promise<void> {\n const s3Key = resolveKey(key);\n if (!s3Key) return;\n\n try {\n await client.deleteObject(s3Key);\n } catch {\n /* S3 delete is idempotent */\n }\n },\n };\n};\n","import { S3mini } from 's3mini';\nimport type { LifecycleContext, SetupFn, Store } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { S3StoreSettings, S3StoreSetup, Types } from './types';\n\n/**\n * Default setup options. EU region by default per AGENT.md.\n */\nexport const DEFAULT_SETUP: Required<S3StoreSetup> = {\n region: 'eu-central-1',\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 S3StoreConfig = Store.Config<Types>;\n\n/**\n * Provision the S3 bucket described in the flow config. Idempotent. Triggered\n * only by the explicit `walkeros setup store.<id>` CLI command.\n *\n * Variant B: only the bucket is created. Encryption, public-access block,\n * versioning, lifecycle rules, and tags are not applied here because `s3mini`\n * does not expose the relevant API operations. Configure those once via the\n * AWS Console or `aws s3api`.\n */\nexport const setup: SetupFn<S3StoreConfig, Store.BaseEnv> = async (\n context: LifecycleContext<S3StoreConfig, Store.BaseEnv>,\n) => {\n const { config, logger } = context;\n\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 assertS3Settings(config.settings);\n const settings = config.settings;\n\n // Region resolution per Open Question #4 (iii):\n // - explicit setup.region wins\n // - else fall back to settings.region when concrete (not 'auto')\n // - else default eu-central-1\n const explicitSetupRegion = readExplicitRegion(config.setup);\n const region = resolveRegion(explicitSetupRegion, settings.region);\n void options; // resolveSetup applies defaults; explicit branch handled separately\n\n const client = new S3mini({\n endpoint: buildEndpoint(settings.endpoint, settings.bucket),\n accessKeyId: settings.accessKeyId,\n secretAccessKey: settings.secretAccessKey,\n region,\n });\n\n const exists = await client.bucketExists();\n if (exists) {\n logger.debug('setup: bucket exists', { bucket: settings.bucket });\n return { bucketCreated: false };\n }\n\n try {\n await client.createBucket();\n logger.info('setup: bucket created', {\n bucket: settings.bucket,\n region,\n });\n return { bucketCreated: true };\n } catch (err: unknown) {\n if (isAlreadyOwnedByYou(err)) {\n logger.debug('setup: bucket created concurrently (race)', {\n bucket: settings.bucket,\n });\n return { bucketCreated: false };\n }\n if (isAlreadyExistsDifferentOwner(err)) {\n throw new Error(\n `S3 bucket name \"${settings.bucket}\" is already in use by another AWS account. ` +\n `Choose a different bucket name in settings.bucket.`,\n );\n }\n throw err;\n }\n};\n\nfunction resolveRegion(\n explicitSetupRegion: string | undefined,\n settingsRegion: string | undefined,\n): string {\n if (\n typeof explicitSetupRegion === 'string' &&\n explicitSetupRegion.length > 0\n ) {\n return explicitSetupRegion;\n }\n if (\n typeof settingsRegion === 'string' &&\n settingsRegion.length > 0 &&\n settingsRegion !== 'auto'\n ) {\n return settingsRegion;\n }\n return DEFAULT_SETUP.region;\n}\n\nfunction readExplicitRegion(raw: S3StoreConfig['setup']): string | undefined {\n if (!isRecord(raw)) return undefined;\n const value = raw.region;\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction assertS3Settings(\n settings: Partial<S3StoreSettings> | undefined,\n): asserts settings is S3StoreSettings {\n if (\n !settings ||\n typeof settings.bucket !== 'string' ||\n settings.bucket.length === 0\n ) {\n throw new Error('setup: settings.bucket is required (non-empty string)');\n }\n if (typeof settings.endpoint !== 'string' || settings.endpoint.length === 0) {\n throw new Error('setup: settings.endpoint is required (non-empty string)');\n }\n if (\n typeof settings.accessKeyId !== 'string' ||\n settings.accessKeyId.length === 0\n ) {\n throw new Error(\n 'setup: settings.accessKeyId is required (non-empty string)',\n );\n }\n if (\n typeof settings.secretAccessKey !== 'string' ||\n settings.secretAccessKey.length === 0\n ) {\n throw new Error(\n 'setup: settings.secretAccessKey is required (non-empty string)',\n );\n }\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nfunction isAlreadyOwnedByYou(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyOwnedByYou');\n}\n\nfunction isAlreadyExistsDifferentOwner(err: unknown): boolean {\n return hasErrorCode(err, 'BucketAlreadyExists');\n}\n\nfunction hasErrorCode(err: unknown, code: string): boolean {\n if (typeof err !== 'object' || err === null) return false;\n const obj: {\n code?: unknown;\n Code?: unknown;\n name?: unknown;\n serviceCode?: unknown;\n } = err;\n return (\n obj.code === code ||\n obj.Code === code ||\n obj.name === code ||\n obj.serviceCode === code\n );\n}\n","import type { Store, SetupFn } from '@walkeros/core';\nimport { storeS3Init } from './store';\nimport { setup } from './setup';\nimport type { Types } from './types';\nimport type { S3StoreConfig } from './setup';\n\nexport { storeS3Init } from './store';\nexport { setup, DEFAULT_SETUP } from './setup';\nexport type { SetupResult, S3StoreConfig } from './setup';\nexport type {\n Env,\n S3StoreSettings,\n S3StoreInitSettings,\n S3StoreSetup,\n Types,\n} from './types';\n\n/**\n * Default export shape for store packages. The CLI (`walkeros setup store.<id>`)\n * reads `default.setup` to find the lifecycle function; the collector reads\n * `default.init` (or the named export) to construct the runtime instance.\n *\n * Soft breaking change: callers using `import storeS3Init from '@walkeros/server-store-s3'`\n * (default import) must now call `.init` on the returned object. Named imports\n * (`import { storeS3Init }`) are unaffected.\n */\nexport interface StoreS3Module {\n type: 's3';\n init: Store.Init<Types>;\n setup: SetupFn<S3StoreConfig, Store.BaseEnv>;\n}\n\nconst storeS3: StoreS3Module = {\n type: 's3',\n init: storeS3Init,\n setup,\n};\n\nexport default storeS3;\n"],"mappings":"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","names":["S3mini","buildEndpoint"]}
|
package/dist/walkerOS.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$meta": {
|
|
3
3
|
"package": "@walkeros/server-store-s3",
|
|
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/s3",
|
|
10
11
|
"source": "https://github.com/elbwalker/walkerOS/tree/main/packages/server/stores/s3/src"
|
|
11
12
|
},
|
|
@@ -79,6 +80,19 @@
|
|
|
79
80
|
},
|
|
80
81
|
"file": true
|
|
81
82
|
},
|
|
83
|
+
"env": {
|
|
84
|
+
"createS3Fetch": {
|
|
85
|
+
"$code": "function d(){const e=new Map;return async(t,s)=>{const{pathname:r}=new URL(function(e){return\"string\"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),n=s?.method??\"GET\";if(r.replace(/\\/+$/,\"\").split(\"/\").length<=2)return new Response(null,{status:200});if(\"PUT\"===n)return e.set(r,(o=s?.body,\"string\"==typeof o?(new TextEncoder).encode(o):o instanceof Uint8Array?new Uint8Array(o):new Uint8Array)),new Response(null,{status:200,headers:{etag:'\"mock\"'}});var o;if(\"DELETE\"===n)return e.delete(r),new Response(null,{status:204});const i=e.get(r);return i?new Response(i,{status:200}):new Response(null,{status:404})}}"
|
|
86
|
+
},
|
|
87
|
+
"push": {
|
|
88
|
+
"fetch": {
|
|
89
|
+
"$code": "async(t,s)=>{const{pathname:r}=new URL(function(e){return\"string\"==typeof e?e:e instanceof URL?e.toString():e.url}(t)),n=s?.method??\"GET\";if(r.replace(/\\/+$/,\"\").split(\"/\").length<=2)return new Response(null,{status:200});if(\"PUT\"===n)return e.set(r,(o=s?.body,\"string\"==typeof o?(new TextEncoder).encode(o):o instanceof Uint8Array?new Uint8Array(o):new Uint8Array)),new Response(null,{status:200,headers:{etag:'\"mock\"'}});var o;if(\"DELETE\"===n)return e.delete(r),new Response(null,{status:204});const i=e.get(r);return i?new Response(i,{status:200}):new Response(null,{status:404})}"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"simulation": [
|
|
93
|
+
"fetch"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
82
96
|
"r2Bucket": {
|
|
83
97
|
"settings": {
|
|
84
98
|
"bucket": "my-bucket",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walkeros/server-store-s3",
|
|
3
3
|
"description": "S3-compatible object 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
|
"s3mini": "^0.9.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {},
|