1+ import fs from "fs/promises" ;
2+ import SCRIPT_KV_NAMESPACE_OBJECT from "worker:kv/namespace" ;
13import SCRIPT_SECRETS_STORE_SECRET from "worker:secrets-store/secret" ;
24import { z } from "zod" ;
3- import { ServiceDesignator , Worker_Binding } from "../../runtime" ;
4- import { KV_PLUGIN , KVOptionsSchema } from "../kv" ;
5- import { PersistenceSchema , Plugin , ProxyNodeBinding } from "../shared" ;
5+ import { Service , Worker_Binding } from "../../runtime" ;
6+ import { SharedBindings } from "../../workers" ;
7+ import { KV_NAMESPACE_OBJECT_CLASS_NAME } from "../kv" ;
8+ import {
9+ getMiniflareObjectBindings ,
10+ getPersistPath ,
11+ objectEntryWorker ,
12+ PersistenceSchema ,
13+ Plugin ,
14+ ProxyNodeBinding ,
15+ SERVICE_LOOPBACK ,
16+ } from "../shared" ;
617
718const SecretsStoreSecretsSchema = z . record (
819 z . object ( {
@@ -21,30 +32,6 @@ export const SecretsStoreSecretsSharedOptionsSchema = z.object({
2132
2233export const SECRET_STORE_PLUGIN_NAME = "secrets-store" ;
2334
24- function getkvNamespacesOptions (
25- secretsStoreSecrets : z . input < typeof SecretsStoreSecretsSchema >
26- ) : z . input < typeof KVOptionsSchema > {
27- // Get unique store ids
28- const storeIds = new Set (
29- Object . values ( secretsStoreSecrets ) . map ( ( store ) => store . store_id )
30- ) ;
31- // Setup a KV Namespace per store id with store id as the binding name
32- const storeIdKvNamespaceEntries = Array . from ( storeIds ) . map ( ( storeId ) => [
33- storeId ,
34- `${ SECRET_STORE_PLUGIN_NAME } :${ storeId } ` ,
35- ] ) ;
36-
37- return {
38- kvNamespaces : Object . fromEntries ( storeIdKvNamespaceEntries ) ,
39- } ;
40- }
41-
42- function isKvBinding (
43- binding : Worker_Binding
44- ) : binding is Worker_Binding & { kvNamespace : ServiceDesignator } {
45- return "kvNamespace" in binding ;
46- }
47-
4835export const SECRET_STORE_PLUGIN : Plugin <
4936 typeof SecretsStoreSecretsOptionsSchema ,
5037 typeof SecretsStoreSecretsSharedOptionsSchema
@@ -80,65 +67,99 @@ export const SECRET_STORE_PLUGIN: Plugin<
8067 ] )
8168 ) ;
8269 } ,
83- async getServices ( { options, sharedOptions, ...restOptions } ) {
84- if ( ! options . secretsStoreSecrets ) {
70+ async getServices ( { options, sharedOptions, tmpPath, unsafeStickyBlobs } ) {
71+ const configs = options . secretsStoreSecrets
72+ ? Object . values ( options . secretsStoreSecrets )
73+ : [ ] ;
74+
75+ if ( configs . length === 0 ) {
8576 return [ ] ;
8677 }
8778
88- const kvServices = await KV_PLUGIN . getServices ( {
89- options : getkvNamespacesOptions ( options . secretsStoreSecrets ) ,
90- sharedOptions : {
91- kvPersist : sharedOptions . secretsStorePersist ,
92- } ,
93- ...restOptions ,
94- } ) ;
95-
96- const kvBindings = await KV_PLUGIN . getBindings (
97- getkvNamespacesOptions ( options . secretsStoreSecrets ) ,
98- restOptions . workerIndex
79+ const persistPath = getPersistPath (
80+ SECRET_STORE_PLUGIN_NAME ,
81+ tmpPath ,
82+ sharedOptions . secretsStorePersist
9983 ) ;
10084
101- if ( ! kvBindings || ! kvBindings . every ( isKvBinding ) ) {
102- throw new Error (
103- "Expected KV plugin to return bindings with kvNamespace defined"
104- ) ;
105- }
106-
107- if ( ! Array . isArray ( kvServices ) ) {
108- throw new Error ( "Expected KV plugin to return an array of services" ) ;
109- }
85+ await fs . mkdir ( persistPath , { recursive : true } ) ;
11086
111- return [
112- ...kvServices ,
113- ...Object . entries ( options . secretsStoreSecrets ) . map < Worker_Binding > (
114- ( [ _ , config ] ) => {
115- return {
116- name : `${ SECRET_STORE_PLUGIN_NAME } :${ config . store_id } :${ config . secret_name } ` ,
117- worker : {
118- compatibilityDate : "2025-01-01" ,
119- modules : [
120- {
121- name : "secret.worker.js" ,
122- esModule : SCRIPT_SECRETS_STORE_SECRET ( ) ,
123- } ,
124- ] ,
125- bindings : [
126- {
127- name : "store" ,
128- kvNamespace : kvBindings . find (
129- // Look up the corresponding KV namespace for the store id
130- ( binding ) => binding . name === config . store_id
131- ) ?. kvNamespace ,
132- } ,
133- {
134- name : "secret_name" ,
135- json : JSON . stringify ( config . secret_name ) ,
136- } ,
137- ] ,
87+ const storageService = {
88+ name : `${ SECRET_STORE_PLUGIN_NAME } :storage` ,
89+ disk : { path : persistPath , writable : true } ,
90+ } satisfies Service ;
91+ const objectService = {
92+ name : `${ SECRET_STORE_PLUGIN_NAME } :ns` ,
93+ worker : {
94+ compatibilityDate : "2023-07-24" ,
95+ compatibilityFlags : [ "nodejs_compat" , "experimental" ] ,
96+ modules : [
97+ {
98+ name : "namespace.worker.js" ,
99+ esModule : SCRIPT_KV_NAMESPACE_OBJECT ( ) ,
100+ } ,
101+ ] ,
102+ durableObjectNamespaces : [
103+ {
104+ className : KV_NAMESPACE_OBJECT_CLASS_NAME ,
105+ uniqueKey : `miniflare-secrets-store-${ KV_NAMESPACE_OBJECT_CLASS_NAME } ` ,
106+ } ,
107+ ] ,
108+ // Store Durable Object SQL databases in persist path
109+ durableObjectStorage : { localDisk : storageService . name } ,
110+ // Bind blob disk directory service to object
111+ bindings : [
112+ {
113+ name : SharedBindings . MAYBE_SERVICE_BLOBS ,
114+ service : { name : storageService . name } ,
115+ } ,
116+ {
117+ name : SharedBindings . MAYBE_SERVICE_LOOPBACK ,
118+ service : { name : SERVICE_LOOPBACK } ,
119+ } ,
120+ ...getMiniflareObjectBindings ( unsafeStickyBlobs ) ,
121+ ] ,
122+ } ,
123+ } satisfies Service ;
124+ const services = configs . flatMap < Service > ( ( config ) => {
125+ const kvNamespaceService = {
126+ name : `${ SECRET_STORE_PLUGIN_NAME } :ns:${ config . store_id } ` ,
127+ worker : objectEntryWorker (
128+ {
129+ serviceName : objectService . name ,
130+ className : KV_NAMESPACE_OBJECT_CLASS_NAME ,
131+ } ,
132+ config . store_id
133+ ) ,
134+ } satisfies Service ;
135+ const secretStoreSecretService = {
136+ name : `${ SECRET_STORE_PLUGIN_NAME } :${ config . store_id } :${ config . secret_name } ` ,
137+ worker : {
138+ compatibilityDate : "2025-01-01" ,
139+ modules : [
140+ {
141+ name : "secret.worker.js" ,
142+ esModule : SCRIPT_SECRETS_STORE_SECRET ( ) ,
143+ } ,
144+ ] ,
145+ bindings : [
146+ {
147+ name : "store" ,
148+ kvNamespace : {
149+ name : kvNamespaceService . name ,
150+ } ,
151+ } ,
152+ {
153+ name : "secret_name" ,
154+ json : JSON . stringify ( config . secret_name ) ,
138155 } ,
139- } ;
140- }
141- ) ,
142- ] ;
156+ ] ,
157+ } ,
158+ } satisfies Service ;
159+
160+ return [ kvNamespaceService , secretStoreSecretService ] ;
161+ } ) ;
162+
163+ return [ ...services , storageService , objectService ] ;
143164 } ,
144165} ;
0 commit comments