@walkeros/server-store-s3 3.0.0-next-1772811722420

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # @walkeros/server-store-s3
2
+
3
+ S3-compatible object storage for walkerOS server flows.
4
+
5
+ Works with AWS S3, Cloudflare R2, Scaleway, DigitalOcean Spaces, Backblaze B2,
6
+ MinIO, and any S3-compatible provider. Uses
7
+ [s3mini](https://github.com/good-lly/s3mini) (~20 KB, zero dependencies).
8
+
9
+ ## Quick Start (Bundled Mode)
10
+
11
+ ```json
12
+ {
13
+ "version": 1,
14
+ "flows": {
15
+ "default": {
16
+ "server": {},
17
+ "stores": {
18
+ "assets": {
19
+ "package": "@walkeros/server-store-s3",
20
+ "config": {
21
+ "settings": {
22
+ "bucket": "my-assets",
23
+ "endpoint": "https://s3.eu-west-1.amazonaws.com",
24
+ "accessKeyId": "$env.S3_ACCESS_KEY",
25
+ "secretAccessKey": "$env.S3_SECRET_KEY",
26
+ "region": "eu-west-1",
27
+ "prefix": "public"
28
+ }
29
+ }
30
+ }
31
+ },
32
+ "transformers": {
33
+ "file": {
34
+ "package": "@walkeros/server-transformer-file",
35
+ "config": { "settings": { "prefix": "/static" } },
36
+ "env": { "store": "$store:assets" }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ ## Integrated Mode
45
+
46
+ ```typescript
47
+ import { storeS3Init } from '@walkeros/server-store-s3';
48
+
49
+ const store = await storeS3Init({
50
+ collector,
51
+ logger,
52
+ id: 'assets',
53
+ config: {
54
+ settings: {
55
+ bucket: 'my-assets',
56
+ endpoint: 'https://s3.eu-west-1.amazonaws.com',
57
+ accessKeyId: process.env.S3_ACCESS_KEY!,
58
+ secretAccessKey: process.env.S3_SECRET_KEY!,
59
+ region: 'eu-west-1',
60
+ prefix: 'public',
61
+ },
62
+ },
63
+ env: {},
64
+ });
65
+
66
+ const file = await store.get('walker.js'); // Buffer | undefined
67
+ await store.set('cache/data.json', Buffer.from('{}'));
68
+ await store.delete('old-file.txt');
69
+ ```
70
+
71
+ ## Configuration
72
+
73
+ | Setting | Type | Required | Default | Description |
74
+ | ----------------- | -------- | -------- | -------- | -------------------------- |
75
+ | `bucket` | `string` | Yes | — | S3 bucket name |
76
+ | `endpoint` | `string` | Yes | — | S3-compatible endpoint URL |
77
+ | `accessKeyId` | `string` | Yes | — | S3 access key ID |
78
+ | `secretAccessKey` | `string` | Yes | — | S3 secret access key |
79
+ | `region` | `string` | No | `"auto"` | AWS region (SigV4 signing) |
80
+ | `prefix` | `string` | No | — | Key prefix for scoping |
81
+
82
+ ## Provider Examples
83
+
84
+ | Provider | Endpoint | Notes |
85
+ | ------------- | -------------------------------------------- | ---------------- |
86
+ | AWS S3 | `https://s3.<region>.amazonaws.com` | Standard |
87
+ | Cloudflare R2 | `https://<account>.r2.cloudflarestorage.com` | No egress fees |
88
+ | Scaleway | `https://s3.<region>.scw.cloud` | EU hosting |
89
+ | DigitalOcean | `https://<region>.digitaloceanspaces.com` | Simple pricing |
90
+ | Backblaze B2 | `https://s3.<region>.backblazeb2.com` | Cheapest storage |
91
+ | MinIO | `http://localhost:9000` | Self-hosted |
92
+
93
+ ## Credentials
94
+
95
+ Use `$env.` references in Flow.Setup to avoid hardcoding secrets:
96
+
97
+ ```json
98
+ {
99
+ "accessKeyId": "$env.S3_ACCESS_KEY",
100
+ "secretAccessKey": "$env.S3_SECRET_KEY"
101
+ }
102
+ ```
103
+
104
+ Unlike the AWS SDK, `s3mini` has no implicit credential chain — `accessKeyId`
105
+ and `secretAccessKey` are always required.
106
+
107
+ ## Managed Deployments (Mode D)
108
+
109
+ This is the recommended store for managed walkerOS deployments. Files live in a
110
+ bucket rather than needing to be baked into a Docker image, enabling hot-swap of
111
+ static assets.
package/dist/dev.d.mts ADDED
@@ -0,0 +1,35 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+ import { Store } from '@walkeros/core';
4
+
5
+ declare const SettingsSchema: z.ZodObject<{
6
+ bucket: z.ZodString;
7
+ endpoint: z.ZodString;
8
+ accessKeyId: z.ZodString;
9
+ secretAccessKey: z.ZodString;
10
+ region: z.ZodDefault<z.ZodString>;
11
+ prefix: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strip>;
13
+ type Settings = z.infer<typeof SettingsSchema>;
14
+
15
+ declare const settings: _walkeros_core_dev.JSONSchema;
16
+
17
+ type index$1_Settings = Settings;
18
+ declare const index$1_SettingsSchema: typeof SettingsSchema;
19
+ declare const index$1_settings: typeof settings;
20
+ declare namespace index$1 {
21
+ export { type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, index$1_settings as settings };
22
+ }
23
+
24
+ /** AWS S3 bucket for serving static files */
25
+ declare const awsAssets: Store.Config;
26
+ /** Cloudflare R2 bucket — no egress fees */
27
+ declare const r2Bucket: Store.Config;
28
+
29
+ declare const index_awsAssets: typeof awsAssets;
30
+ declare const index_r2Bucket: typeof r2Bucket;
31
+ declare namespace index {
32
+ export { index_awsAssets as awsAssets, index_r2Bucket as r2Bucket };
33
+ }
34
+
35
+ export { index as examples, index$1 as schemas };
package/dist/dev.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import * as _walkeros_core_dev from '@walkeros/core/dev';
2
+ import { z } from '@walkeros/core/dev';
3
+ import { Store } from '@walkeros/core';
4
+
5
+ declare const SettingsSchema: z.ZodObject<{
6
+ bucket: z.ZodString;
7
+ endpoint: z.ZodString;
8
+ accessKeyId: z.ZodString;
9
+ secretAccessKey: z.ZodString;
10
+ region: z.ZodDefault<z.ZodString>;
11
+ prefix: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strip>;
13
+ type Settings = z.infer<typeof SettingsSchema>;
14
+
15
+ declare const settings: _walkeros_core_dev.JSONSchema;
16
+
17
+ type index$1_Settings = Settings;
18
+ declare const index$1_SettingsSchema: typeof SettingsSchema;
19
+ declare const index$1_settings: typeof settings;
20
+ declare namespace index$1 {
21
+ export { type index$1_Settings as Settings, index$1_SettingsSchema as SettingsSchema, index$1_settings as settings };
22
+ }
23
+
24
+ /** AWS S3 bucket for serving static files */
25
+ declare const awsAssets: Store.Config;
26
+ /** Cloudflare R2 bucket — no egress fees */
27
+ declare const r2Bucket: Store.Config;
28
+
29
+ declare const index_awsAssets: typeof awsAssets;
30
+ declare const index_r2Bucket: typeof r2Bucket;
31
+ declare namespace index {
32
+ export { index_awsAssets as awsAssets, index_r2Bucket as r2Bucket };
33
+ }
34
+
35
+ export { index as examples, index$1 as schemas };
package/dist/dev.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e,s=Object.defineProperty,t=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,c=Object.prototype.hasOwnProperty,n=(e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})},o={};n(o,{examples:()=>b,schemas:()=>i}),module.exports=(e=o,((e,n,o,i)=>{if(n&&"object"==typeof n||"function"==typeof n)for(let a of r(n))c.call(e,a)||a===o||s(e,a,{get:()=>n[a],enumerable:!(i=t(n,a))||i.enumerable});return e})(s({},"__esModule",{value:!0}),e));var i={};n(i,{SettingsSchema:()=>u,settings:()=>d});var a=require("@walkeros/core/dev"),p=require("@walkeros/core/dev"),u=p.z.object({bucket:p.z.string().min(1).describe("S3 bucket name"),endpoint:p.z.string().url().describe("S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)"),accessKeyId:p.z.string().min(1).describe("S3 access key ID"),secretAccessKey:p.z.string().min(1).describe("S3 secret access key"),region:p.z.string().default("auto").describe("AWS region for SigV4 signing"),prefix:p.z.string().optional().describe("Key prefix prepended to all store keys for scoping")}),d=(0,a.zodToSchema)(u),b={};n(b,{awsAssets:()=>g,r2Bucket:()=>l});var g={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"}},l={settings:{bucket:"my-bucket",endpoint:"https://ACCOUNT_ID.r2.cloudflarestorage.com",accessKeyId:"$env.R2_ACCESS_KEY",secretAccessKey:"$env.R2_SECRET_KEY"}};//# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dev.ts","../src/schemas/index.ts","../src/schemas/settings.ts","../src/examples/index.ts"],"sourcesContent":["export * as schemas from './schemas';\nexport * as examples from './examples';\n","import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\n\nexport { SettingsSchema, type Settings } from './settings';\n\nexport const settings = zodToSchema(SettingsSchema);\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 type { Store } from '@walkeros/core';\n\n/** AWS S3 bucket for serving static files */\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};\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};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;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;;;ADZM,IAAM,eAAW,yBAAY,cAAc;;;AELlD;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,YAA0B;AAAA,EACrC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;AAGO,IAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AACF;","names":["import_dev"]}
package/dist/dev.mjs ADDED
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,s=(s,t)=>{for(var r in t)e(s,r,{get:t[r],enumerable:!0})},t={};s(t,{SettingsSchema:()=>i,settings:()=>n});import{zodToSchema as r}from"@walkeros/core/dev";import{z as c}from"@walkeros/core/dev";var i=c.object({bucket:c.string().min(1).describe("S3 bucket name"),endpoint:c.string().url().describe("S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)"),accessKeyId:c.string().min(1).describe("S3 access key ID"),secretAccessKey:c.string().min(1).describe("S3 secret access key"),region:c.string().default("auto").describe("AWS region for SigV4 signing"),prefix:c.string().optional().describe("Key prefix prepended to all store keys for scoping")}),n=r(i),o={};s(o,{awsAssets:()=>a,r2Bucket:()=>p});var a={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"}},p={settings:{bucket:"my-bucket",endpoint:"https://ACCOUNT_ID.r2.cloudflarestorage.com",accessKeyId:"$env.R2_ACCESS_KEY",secretAccessKey:"$env.R2_SECRET_KEY"}};export{o as examples,t as schemas};//# sourceMappingURL=dev.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/examples/index.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\n\nexport { SettingsSchema, type Settings } from './settings';\n\nexport const settings = zodToSchema(SettingsSchema);\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 type { Store } from '@walkeros/core';\n\n/** AWS S3 bucket for serving static files */\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};\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};\n"],"mappings":";;;;;;;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;;;ADZM,IAAM,WAAW,YAAY,cAAc;;;AELlD;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,YAA0B;AAAA,EACrC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;AAGO,IAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AACF;","names":[]}
@@ -0,0 +1,38 @@
1
+ import { Store } from '@walkeros/core';
2
+
3
+ interface S3StoreSettings {
4
+ /** S3 bucket name */
5
+ bucket: string;
6
+ /**
7
+ * S3-compatible endpoint URL (required).
8
+ *
9
+ * Examples:
10
+ * - AWS S3: "https://s3.us-east-1.amazonaws.com"
11
+ * - Cloudflare R2: "https://<accountId>.r2.cloudflarestorage.com"
12
+ * - Scaleway: "https://s3.fr-par.scw.cloud"
13
+ * - DigitalOcean: "https://nyc3.digitaloceanspaces.com"
14
+ * - MinIO: "http://localhost:9000"
15
+ */
16
+ endpoint: string;
17
+ /** S3 access key ID */
18
+ accessKeyId: string;
19
+ /** S3 secret access key */
20
+ secretAccessKey: string;
21
+ /**
22
+ * AWS region. Most non-AWS providers ignore this but SigV4 signing requires it.
23
+ * @default "auto"
24
+ */
25
+ region?: string;
26
+ /**
27
+ * Key prefix prepended to all store keys.
28
+ * Use to scope the store to a subdirectory within the bucket.
29
+ * No leading/trailing slash needed — normalized automatically.
30
+ *
31
+ * @example "public/assets" → get("walker.js") looks up "public/assets/walker.js"
32
+ */
33
+ prefix?: string;
34
+ }
35
+
36
+ declare const storeS3Init: Store.Init<Store.Types<S3StoreSettings>>;
37
+
38
+ export { type S3StoreSettings, storeS3Init as default, storeS3Init };
@@ -0,0 +1,38 @@
1
+ import { Store } from '@walkeros/core';
2
+
3
+ interface S3StoreSettings {
4
+ /** S3 bucket name */
5
+ bucket: string;
6
+ /**
7
+ * S3-compatible endpoint URL (required).
8
+ *
9
+ * Examples:
10
+ * - AWS S3: "https://s3.us-east-1.amazonaws.com"
11
+ * - Cloudflare R2: "https://<accountId>.r2.cloudflarestorage.com"
12
+ * - Scaleway: "https://s3.fr-par.scw.cloud"
13
+ * - DigitalOcean: "https://nyc3.digitaloceanspaces.com"
14
+ * - MinIO: "http://localhost:9000"
15
+ */
16
+ endpoint: string;
17
+ /** S3 access key ID */
18
+ accessKeyId: string;
19
+ /** S3 secret access key */
20
+ secretAccessKey: string;
21
+ /**
22
+ * AWS region. Most non-AWS providers ignore this but SigV4 signing requires it.
23
+ * @default "auto"
24
+ */
25
+ region?: string;
26
+ /**
27
+ * Key prefix prepended to all store keys.
28
+ * Use to scope the store to a subdirectory within the bucket.
29
+ * No leading/trailing slash needed — normalized automatically.
30
+ *
31
+ * @example "public/assets" → get("walker.js") looks up "public/assets/walker.js"
32
+ */
33
+ prefix?: string;
34
+ }
35
+
36
+ declare const storeS3Init: Store.Init<Store.Types<S3StoreSettings>>;
37
+
38
+ export { type S3StoreSettings, storeS3Init as default, storeS3Init };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e,t=Object.defineProperty,r=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,c=Object.prototype.hasOwnProperty,s={};((e,r)=>{for(var n in r)t(e,n,{get:r[n],enumerable:!0})})(s,{default:()=>o,storeS3Init:()=>o}),module.exports=(e=s,((e,s,i,o)=>{if(s&&"object"==typeof s||"function"==typeof s)for(let a of n(s))c.call(e,a)||a===i||t(e,a,{get:()=>s[a],enumerable:!(o=r(s,a))||o.enumerable});return e})(t({},"__esModule",{value:!0}),e));var i=require("s3mini");var o=e=>{const t=e.config.settings,r=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(t.prefix),n=new i.S3mini({endpoint:(c=t.endpoint,s=t.bucket,`${c.replace(/\/+$/,"")}/${s}`),accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey,region:t.region||"auto"});var c,s;function o(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})}return{type:"s3",config:e.config,async get(e){const t=o(e);if(t)try{const e=await n.getObjectArrayBuffer(t);if(!e)return;return Buffer.from(e)}catch(e){return}},async set(e,t){const r=o(e);r&&await n.putObject(r,t)},async delete(e){const t=o(e);if(t)try{await n.deleteObject(t)}catch(e){}}}};//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/store.ts"],"sourcesContent":["export { storeS3Init } from './store';\nexport type { S3StoreSettings } from './types';\nexport { storeS3Init as default } from './store';\n","import { S3mini } from 's3mini';\nimport type { Store } from '@walkeros/core';\nimport type { S3StoreSettings } from './types';\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\n\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nexport const storeS3Init: Store.Init<Store.Types<S3StoreSettings>> = (\n context,\n) => {\n const settings = context.config.settings as S3StoreSettings;\n const prefix = normalizePrefix(settings.prefix);\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 return {\n type: 's3',\n config: context.config as Store.Config<Store.Types<S3StoreSettings>>,\n\n async get(key: string): Promise<Buffer | undefined> {\n const s3Key = resolveKey(key);\n if (!s3Key) return undefined;\n\n try {\n const arrayBuffer = await client.getObjectArrayBuffer(s3Key);\n if (!arrayBuffer) return undefined;\n return Buffer.from(arrayBuffer);\n } catch {\n return undefined;\n }\n },\n\n async set(key: string, value: unknown): Promise<void> {\n const s3Key = resolveKey(key);\n if (!s3Key) return;\n\n await client.putObject(s3Key, value as Buffer);\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAuB;AAIvB,SAAS,WAAW,KAAsB;AACxC,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AAChE,SAAO,CAAC,IAAI,MAAM,OAAO,EAAE,SAAS,IAAI;AAC1C;AAEA,SAAS,gBAAgB,QAAyB;AAChD,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,QAAQ,cAAc,EAAE;AAC/C,SAAO,UAAU,UAAU,MAAM;AACnC;AAEA,SAAS,cAAc,UAAkB,QAAwB;AAC/D,QAAM,OAAO,SAAS,QAAQ,QAAQ,EAAE;AACxC,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAEO,IAAM,cAAwD,CACnE,YACG;AACH,QAAM,WAAW,QAAQ,OAAO;AAChC,QAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAM,SAAS,IAAI,qBAAO;AAAA,IACxB,UAAU,cAAc,SAAS,UAAU,SAAS,MAAM;AAAA,IAC1D,aAAa,SAAS;AAAA,IACtB,iBAAiB,SAAS;AAAA,IAC1B,QAAQ,SAAS,UAAU;AAAA,EAC7B,CAAC;AAED,WAAS,WAAW,KAAiC;AACnD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAQ,OAAO,KAAK,wBAAwB,EAAE,IAAI,CAAC;AACnD,aAAO;AAAA,IACT;AACA,WAAO,SAAS;AAAA,EAClB;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,QAAQ;AAAA,IAEhB,MAAM,IAAI,KAA0C;AAClD,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,CAAC,MAAO,QAAO;AAEnB,UAAI;AACF,cAAM,cAAc,MAAM,OAAO,qBAAqB,KAAK;AAC3D,YAAI,CAAC,YAAa,QAAO;AACzB,eAAO,OAAO,KAAK,WAAW;AAAA,MAChC,SAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,IAAI,KAAa,OAA+B;AACpD,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,CAAC,MAAO;AAEZ,YAAM,OAAO,UAAU,OAAO,KAAe;AAAA,IAC/C;AAAA,IAEA,MAAM,OAAO,KAA4B;AACvC,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,CAAC,MAAO;AAEZ,UAAI;AACF,cAAM,OAAO,aAAa,KAAK;AAAA,MACjC,SAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{S3mini as e}from"s3mini";var t=t=>{const r=t.config.settings,n=function(e){if(!e)return"";const t=e.replace(/^\/+|\/+$/g,"");return t?t+"/":""}(r.prefix),c=new e({endpoint:(s=r.endpoint,i=r.bucket,`${s.replace(/\/+$/,"")}/${i}`),accessKeyId:r.accessKeyId,secretAccessKey:r.secretAccessKey,region:r.region||"auto"});var s,i;function a(e){if(function(e){return!(!e||e.startsWith("/")||e.startsWith("\\")||e.split(/[/\\]/).includes(".."))}(e))return n+e;t.logger.warn("Invalid key rejected",{key:e})}return{type:"s3",config:t.config,async get(e){const t=a(e);if(t)try{const e=await c.getObjectArrayBuffer(t);if(!e)return;return Buffer.from(e)}catch(e){return}},async set(e,t){const r=a(e);r&&await c.putObject(r,t)},async delete(e){const t=a(e);if(t)try{await c.deleteObject(t)}catch(e){}}}};export{t as default,t as storeS3Init};//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store.ts"],"sourcesContent":["import { S3mini } from 's3mini';\nimport type { Store } from '@walkeros/core';\nimport type { S3StoreSettings } from './types';\n\nfunction isValidKey(key: string): boolean {\n if (!key || key.startsWith('/') || key.startsWith('\\\\')) return false;\n return !key.split(/[/\\\\]/).includes('..');\n}\n\nfunction normalizePrefix(prefix?: string): string {\n if (!prefix) return '';\n const trimmed = prefix.replace(/^\\/+|\\/+$/g, '');\n return trimmed ? trimmed + '/' : '';\n}\n\nfunction buildEndpoint(endpoint: string, bucket: string): string {\n const base = endpoint.replace(/\\/+$/, '');\n return `${base}/${bucket}`;\n}\n\nexport const storeS3Init: Store.Init<Store.Types<S3StoreSettings>> = (\n context,\n) => {\n const settings = context.config.settings as S3StoreSettings;\n const prefix = normalizePrefix(settings.prefix);\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 return {\n type: 's3',\n config: context.config as Store.Config<Store.Types<S3StoreSettings>>,\n\n async get(key: string): Promise<Buffer | undefined> {\n const s3Key = resolveKey(key);\n if (!s3Key) return undefined;\n\n try {\n const arrayBuffer = await client.getObjectArrayBuffer(s3Key);\n if (!arrayBuffer) return undefined;\n return Buffer.from(arrayBuffer);\n } catch {\n return undefined;\n }\n },\n\n async set(key: string, value: unknown): Promise<void> {\n const s3Key = resolveKey(key);\n if (!s3Key) return;\n\n await client.putObject(s3Key, value as Buffer);\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"],"mappings":";AAAA,SAAS,cAAc;AAIvB,SAAS,WAAW,KAAsB;AACxC,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,EAAG,QAAO;AAChE,SAAO,CAAC,IAAI,MAAM,OAAO,EAAE,SAAS,IAAI;AAC1C;AAEA,SAAS,gBAAgB,QAAyB;AAChD,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,QAAQ,cAAc,EAAE;AAC/C,SAAO,UAAU,UAAU,MAAM;AACnC;AAEA,SAAS,cAAc,UAAkB,QAAwB;AAC/D,QAAM,OAAO,SAAS,QAAQ,QAAQ,EAAE;AACxC,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAEO,IAAM,cAAwD,CACnE,YACG;AACH,QAAM,WAAW,QAAQ,OAAO;AAChC,QAAM,SAAS,gBAAgB,SAAS,MAAM;AAE9C,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,UAAU,cAAc,SAAS,UAAU,SAAS,MAAM;AAAA,IAC1D,aAAa,SAAS;AAAA,IACtB,iBAAiB,SAAS;AAAA,IAC1B,QAAQ,SAAS,UAAU;AAAA,EAC7B,CAAC;AAED,WAAS,WAAW,KAAiC;AACnD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAQ,OAAO,KAAK,wBAAwB,EAAE,IAAI,CAAC;AACnD,aAAO;AAAA,IACT;AACA,WAAO,SAAS;AAAA,EAClB;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,QAAQ;AAAA,IAEhB,MAAM,IAAI,KAA0C;AAClD,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,CAAC,MAAO,QAAO;AAEnB,UAAI;AACF,cAAM,cAAc,MAAM,OAAO,qBAAqB,KAAK;AAC3D,YAAI,CAAC,YAAa,QAAO;AACzB,eAAO,OAAO,KAAK,WAAW;AAAA,MAChC,SAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,IAAI,KAAa,OAA+B;AACpD,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,CAAC,MAAO;AAEZ,YAAM,OAAO,UAAU,OAAO,KAAe;AAAA,IAC/C;AAAA,IAEA,MAAM,OAAO,KAA4B;AACvC,YAAM,QAAQ,WAAW,GAAG;AAC5B,UAAI,CAAC,MAAO;AAEZ,UAAI;AACF,cAAM,OAAO,aAAa,KAAK;AAAA,MACjC,SAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,73 @@
1
+ {
2
+ "$meta": {
3
+ "package": "@walkeros/server-store-s3",
4
+ "version": "2.1.0",
5
+ "type": "store",
6
+ "platform": "server"
7
+ },
8
+ "schemas": {
9
+ "settings": {
10
+ "$schema": "http://json-schema.org/draft-07/schema#",
11
+ "type": "object",
12
+ "properties": {
13
+ "bucket": {
14
+ "type": "string",
15
+ "minLength": 1,
16
+ "description": "S3 bucket name"
17
+ },
18
+ "endpoint": {
19
+ "type": "string",
20
+ "format": "uri",
21
+ "description": "S3-compatible endpoint URL (like https://s3.eu-west-1.amazonaws.com)"
22
+ },
23
+ "accessKeyId": {
24
+ "type": "string",
25
+ "minLength": 1,
26
+ "description": "S3 access key ID"
27
+ },
28
+ "secretAccessKey": {
29
+ "type": "string",
30
+ "minLength": 1,
31
+ "description": "S3 secret access key"
32
+ },
33
+ "region": {
34
+ "default": "auto",
35
+ "description": "AWS region for SigV4 signing",
36
+ "type": "string"
37
+ },
38
+ "prefix": {
39
+ "description": "Key prefix prepended to all store keys for scoping",
40
+ "type": "string"
41
+ }
42
+ },
43
+ "required": [
44
+ "bucket",
45
+ "endpoint",
46
+ "accessKeyId",
47
+ "secretAccessKey",
48
+ "region"
49
+ ],
50
+ "additionalProperties": false
51
+ }
52
+ },
53
+ "examples": {
54
+ "awsAssets": {
55
+ "settings": {
56
+ "bucket": "my-assets",
57
+ "endpoint": "https://s3.eu-west-1.amazonaws.com",
58
+ "accessKeyId": "$env.S3_ACCESS_KEY",
59
+ "secretAccessKey": "$env.S3_SECRET_KEY",
60
+ "region": "eu-west-1",
61
+ "prefix": "public"
62
+ }
63
+ },
64
+ "r2Bucket": {
65
+ "settings": {
66
+ "bucket": "my-bucket",
67
+ "endpoint": "https://ACCOUNT_ID.r2.cloudflarestorage.com",
68
+ "accessKeyId": "$env.R2_ACCESS_KEY",
69
+ "secretAccessKey": "$env.R2_SECRET_KEY"
70
+ }
71
+ }
72
+ }
73
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@walkeros/server-store-s3",
3
+ "description": "S3-compatible object storage for walkerOS server flows",
4
+ "version": "3.0.0-next-1772811722420",
5
+ "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ },
15
+ "./dev": {
16
+ "types": "./dist/dev.d.ts",
17
+ "import": "./dist/dev.mjs",
18
+ "require": "./dist/dev.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist/**"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup --silent",
26
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
27
+ "dev": "jest --watchAll --colors",
28
+ "typecheck": "tsc --noEmit",
29
+ "lint": "eslint \"**/*.ts*\"",
30
+ "test": "jest",
31
+ "update": "npx npm-check-updates -u && npm update"
32
+ },
33
+ "dependencies": {
34
+ "s3mini": "^0.9.1"
35
+ },
36
+ "peerDependencies": {
37
+ "@walkeros/core": "2.2.0-next-1772811722420"
38
+ },
39
+ "devDependencies": {
40
+ "@walkeros/core": "2.2.0-next-1772811722420"
41
+ },
42
+ "repository": {
43
+ "url": "git+https://github.com/elbwalker/walkerOS.git",
44
+ "directory": "packages/server/stores/s3"
45
+ },
46
+ "author": "elbwalker <hello@elbwalker.com>",
47
+ "homepage": "https://github.com/elbwalker/walkerOS#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/elbwalker/walkerOS/issues"
50
+ },
51
+ "walkerOS": {
52
+ "type": "store",
53
+ "platform": "server"
54
+ },
55
+ "keywords": [
56
+ "walkerOS",
57
+ "store",
58
+ "s3",
59
+ "server"
60
+ ],
61
+ "funding": [
62
+ {
63
+ "type": "GitHub Sponsors",
64
+ "url": "https://github.com/sponsors/elbwalker"
65
+ }
66
+ ]
67
+ }