@reboot-dev/reboot 0.23.0 → 0.24.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/index.d.ts CHANGED
@@ -50,6 +50,7 @@ export declare class Context {
50
50
  get auth(): Auth | null;
51
51
  get stateId(): any;
52
52
  get iteration(): any;
53
+ get cookie(): any;
53
54
  generateIdempotentStateId(stateType: string, serviceName: string, method: string, idempotency: IdempotencyOptions): Promise<any>;
54
55
  }
55
56
  export declare class ReaderContext extends Context {
@@ -117,8 +118,8 @@ export declare abstract class TokenVerifier {
117
118
  * Returns:
118
119
  * `Auth` information if the token is valid, null otherwise.
119
120
  */
120
- abstract verifyToken(context: ReaderContext, token: string): Promise<Auth | null>;
121
- _verifyToken(context: ReaderContext, token: string): Promise<Uint8Array | null>;
121
+ abstract verifyToken(context: ReaderContext, token?: string): Promise<Auth | null>;
122
+ _verifyToken(context: ReaderContext, token?: string): Promise<Uint8Array | null>;
122
123
  }
123
124
  export type AuthorizerDecision = errors_pb.Unauthenticated | errors_pb.PermissionDenied | errors_pb.Ok;
124
125
  /**
@@ -171,20 +172,44 @@ export declare class Loop {
171
172
  export declare function retry_reactively_until(context: WorkflowContext, condition: () => Promise<boolean>): Promise<void>;
172
173
  export declare function retry_reactively_until<T>(context: WorkflowContext, condition: () => Promise<false | Exclude<T, boolean>>): Promise<Exclude<T, boolean>>;
173
174
  export declare function atMostOnce(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<void>, options?: {
174
- parse: undefined;
175
+ stringify?: undefined;
176
+ parse?: undefined;
177
+ validate?: undefined;
175
178
  }): Promise<void>;
176
179
  export declare function atMostOnce<T>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<T>, options: {
177
- parse: (value: any) => T;
180
+ stringify?: (result: T) => string;
181
+ parse: (value: string) => T;
182
+ validate?: undefined;
183
+ } | {
184
+ stringify?: (result: T) => string;
185
+ parse?: undefined;
186
+ validate: (result: T) => boolean;
178
187
  }): Promise<T>;
179
188
  export declare function atLeastOnce(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<void>, options?: {
180
- parse: undefined;
189
+ stringify?: undefined;
190
+ parse?: undefined;
191
+ validate?: undefined;
181
192
  }): Promise<void>;
182
193
  export declare function atLeastOnce<T>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<T>, options: {
183
- parse: (value: any) => T;
194
+ stringify?: (result: T) => string;
195
+ parse: (value: string) => T;
196
+ validate?: undefined;
197
+ } | {
198
+ stringify?: (result: T) => string;
199
+ parse?: undefined;
200
+ validate: (result: T) => boolean;
184
201
  }): Promise<T>;
185
202
  export declare function until(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<boolean>, options?: {
186
- parse: undefined;
203
+ stringify?: undefined;
204
+ parse?: undefined;
205
+ validate?: undefined;
187
206
  }): Promise<void>;
188
207
  export declare function until<T>(idempotencyAlias: string, context: WorkflowContext, callable: () => Promise<false | Exclude<T, boolean>>, options: {
189
- parse: (value: any) => T;
208
+ stringify?: (result: T) => string;
209
+ parse: (value: string) => T;
210
+ validate?: undefined;
211
+ } | {
212
+ stringify?: (result: T) => string;
213
+ parse?: undefined;
214
+ validate: (result: T) => boolean;
190
215
  }): Promise<Exclude<T, boolean>>;
package/index.js CHANGED
@@ -49,6 +49,12 @@ if (process != null) {
49
49
  // exits due to a SIGINT will exit with a code of 130.
50
50
  checkIfNoOtherListenersAndIfSoExit("SIGINT", 130);
51
51
  });
52
+ process.on("unhandledRejection", (reason, promise) => {
53
+ // We install a slightly quieter unhandled-rejection handler because the
54
+ // native portion of Reboot renders useful error messages before raising.
55
+ console.error("Exiting:", reason);
56
+ checkIfNoOtherListenersAndIfSoExit("unhandledRejection", 1);
57
+ });
52
58
  }
53
59
  export class Reboot {
54
60
  constructor() {
@@ -169,6 +175,9 @@ export class Context {
169
175
  get iteration() {
170
176
  return reboot_native.Context_iteration(__classPrivateFieldGet(this, _Context_external, "f"));
171
177
  }
178
+ get cookie() {
179
+ return reboot_native.Context_cookie(__classPrivateFieldGet(this, _Context_external, "f"));
180
+ }
172
181
  async generateIdempotentStateId(stateType, serviceName, method, idempotency) {
173
182
  return reboot_native.Context_generateIdempotentStateId(__classPrivateFieldGet(this, _Context_external, "f"), stateType, serviceName, method, idempotency);
174
183
  }
@@ -395,43 +404,55 @@ export async function retry_reactively_until(context, condition) {
395
404
  });
396
405
  return t;
397
406
  }
398
- async function atLeastOrMostOnce(idempotencyAlias, context, callable, options) {
399
- let t = undefined;
407
+ async function atLeastOrMostOnce(idempotencyAlias, context, callable, { stringify = JSON.stringify, parse = JSON.parse, validate, atMostOnce, }) {
408
+ assert(stringify !== undefined);
409
+ assert(parse !== undefined);
410
+ assert(atMostOnce !== undefined);
400
411
  const result = await reboot_native.atLeastOrMostOnce(context.__external, idempotencyAlias, async () => {
401
- t = await callable();
412
+ const t = await callable();
402
413
  if (t !== undefined) {
403
- if (options.parse === undefined) {
404
- throw new Error("Required 'parse' property in 'options' is undefined");
405
- }
406
- // NOTE: we've decided not to stringify and parse `t` using
407
- // `options.parse` now to avoid the extra overhead, but it
408
- // might catch some bugs _before_ anything gets persisted and
409
- // users may prefer that tradeoff.
410
- return JSON.stringify(t);
414
+ // NOTE: to differentiate `callable` returning `void` (or
415
+ // explicitly `undefined`) from `stringify` returning an empty
416
+ // string we use `{ value: stringify(t) }`.
417
+ const result = { value: stringify(t) };
418
+ return JSON.stringify(result);
419
+ }
420
+ // Fail early if the developer thinks that they have some value
421
+ // that they want to validate but we got `undefined`.
422
+ if (validate !== undefined) {
423
+ throw new Error("Not expecting `validate` as you are returning `void` (or explicitly `undefined`); did you mean to return a value (or if you want to explicitly return the absence of a value use `null`)");
411
424
  }
412
425
  // NOTE: using the empty string to represent a `callable`
413
- // returning void or explicitly `undefined`.
426
+ // returning `void` (or explicitly `undefined`).
414
427
  return "";
415
- }, options.atMostOnce);
416
- if (t !== undefined) {
417
- return t;
418
- }
428
+ }, atMostOnce);
429
+ // NOTE: we parse and validate `value` every time, even the first
430
+ // time, so as to catch bugs where the `value` returned from
431
+ // `callable` might not parse or be valid. We will have already
432
+ // persisted `result`, so in the event of a bug the developer will
433
+ // have to change the idempotency alias so that `callable` is
434
+ // re-executed. These semantics are the same as Python (although
435
+ // Python uses the `type` keyword argument instead of the
436
+ // `parse` and `validate` properties we use here).
419
437
  assert(result !== undefined);
420
438
  if (result !== "") {
421
- if (options.parse === undefined) {
422
- throw new Error("Required 'parse' property in 'options' is undefined");
439
+ const { value } = JSON.parse(result);
440
+ const t = parse(value);
441
+ if (parse !== JSON.parse) {
442
+ if (validate === undefined) {
443
+ // TODO: link to docs about why this is required, when those docs exist.
444
+ throw new Error("Missing `validate` property");
445
+ }
446
+ else if (!validate(t)) {
447
+ throw new Error("Failed to validate memoized result");
448
+ }
423
449
  }
424
- return options.parse(JSON.parse(result));
425
- }
426
- assert(result === "");
427
- // Let end user decide what they want to do with `undefined` if
428
- // they specify `options.parse`.
429
- if (options.parse !== undefined) {
430
- return options.parse(undefined);
450
+ return t;
431
451
  }
432
- // Otherwise `callable` must return void (undefined), fall through.
452
+ // Otherwise `callable` must have returned void (or explicitly
453
+ // `undefined`), fall through.
433
454
  }
434
- export async function atMostOnce(idempotencyAlias, context, callable, options = { parse: undefined }) {
455
+ export async function atMostOnce(idempotencyAlias, context, callable, options = { validate: undefined }) {
435
456
  try {
436
457
  return await atLeastOrMostOnce(idempotencyAlias, context, callable, {
437
458
  ...options,
@@ -445,13 +466,13 @@ export async function atMostOnce(idempotencyAlias, context, callable, options =
445
466
  throw e;
446
467
  }
447
468
  }
448
- export async function atLeastOnce(idempotencyAlias, context, callable, options = { parse: undefined }) {
469
+ export async function atLeastOnce(idempotencyAlias, context, callable, options = { validate: undefined }) {
449
470
  return atLeastOrMostOnce(idempotencyAlias, context, callable, {
450
471
  ...options,
451
472
  atMostOnce: false,
452
473
  });
453
474
  }
454
- export async function until(idempotencyAlias, context, callable, options = { parse: undefined }) {
475
+ export async function until(idempotencyAlias, context, callable, options = { validate: undefined }) {
455
476
  // TODO(benh): figure out how to not use `as` type assertions here
456
477
  // to appease the TypeScript compiler which otherwise isn't happy
457
478
  // with passing on these types.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "@bufbuild/protobuf": "1.3.2",
4
4
  "@bufbuild/protoplugin": "1.3.2",
5
5
  "@bufbuild/protoc-gen-es": "1.3.2",
6
- "@reboot-dev/reboot-api": "0.23.0",
6
+ "@reboot-dev/reboot-api": "0.24.0",
7
7
  "chalk": "^4.1.2",
8
8
  "node-addon-api": "^7.0.0",
9
9
  "node-gyp": ">=10.2.0",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "type": "module",
16
16
  "name": "@reboot-dev/reboot",
17
- "version": "0.23.0",
17
+ "version": "0.24.0",
18
18
  "description": "npm package for Reboot",
19
19
  "scripts": {
20
20
  "postinstall": "rbt || exit 0",
@@ -54,6 +54,7 @@
54
54
  "reboot_native.cc",
55
55
  "reboot_native.cjs",
56
56
  "reboot_native.d.ts",
57
+ "secrets",
57
58
  "utils",
58
59
  "venv.d.ts",
59
60
  "venv.js",
package/reboot_native.cc CHANGED
@@ -534,13 +534,13 @@ class NapiSafeReference {
534
534
  public:
535
535
  NapiSafeReference(T t)
536
536
  : _reference(
537
- new Napi::Reference<T>(Napi::Persistent(std::move(t))),
538
- NapiReferenceDeleter()) {}
537
+ new Napi::Reference<T>(Napi::Persistent(std::move(t))),
538
+ NapiReferenceDeleter()) {}
539
539
 
540
540
  NapiSafeReference(Napi::Reference<T>&& reference)
541
541
  : _reference(
542
- new Napi::Reference<T>(std::move(reference)),
543
- NapiReferenceDeleter()) {}
542
+ new Napi::Reference<T>(std::move(reference)),
543
+ NapiReferenceDeleter()) {}
544
544
 
545
545
  // Helper for getting the value of the reference. We require a
546
546
  // `Napi::Env` to ensure we only try and get the value from within a
@@ -1156,7 +1156,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1156
1156
  [](py::object self,
1157
1157
  py::object py_reader_context,
1158
1158
  py::object py_aborted,
1159
- const std::string& token) {
1159
+ py::object token) {
1160
1160
  py::object py_future =
1161
1161
  py::module::import("asyncio").attr("Future")();
1162
1162
 
@@ -1168,7 +1168,7 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1168
1168
  [js_token_verifier_reference,
1169
1169
  py_reader_context = new py::object(py_reader_context),
1170
1170
  py_aborted = new py::object(py_aborted),
1171
- token,
1171
+ token = new py::object(token),
1172
1172
  py_future = new py::object(py_future)](Napi::Env env) {
1173
1173
  std::vector<Napi::Value> js_args;
1174
1174
 
@@ -1179,7 +1179,12 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
1179
1179
  py_aborted,
1180
1180
  "reader"));
1181
1181
 
1182
- js_args.push_back(Napi::String::New(env, token));
1182
+ if (token->is_none()) {
1183
+ js_args.push_back(env.Undefined());
1184
+ } else {
1185
+ js_args.push_back(
1186
+ Napi::String::New(env, std::string(py::str(*token))));
1187
+ }
1183
1188
 
1184
1189
  Napi::Object js_token_verifier =
1185
1190
  js_token_verifier_reference->Value(env);
@@ -1694,19 +1699,9 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
1694
1699
  }
1695
1700
 
1696
1701
 
1697
- Napi::Value Future_await(const Napi::CallbackInfo& info) {
1702
+ Napi::Value Task_await(const Napi::CallbackInfo& info) {
1698
1703
  Napi::Object js_args = info[0].As<Napi::Object>();
1699
1704
 
1700
- // NOTE: we immediately get a safe reference to the `Napi::External`
1701
- // so that Node will not garbage collect it and the `py::object*` we
1702
- // get out of it will remain valid.
1703
- auto js_external_service = NapiSafeReference(
1704
- js_args.Get("external").As<Napi::External<py::object>>());
1705
-
1706
- // CHECK(js_external_service.CheckTypeTag(...));
1707
-
1708
- py::object* py_service = js_external_service.Value(info.Env()).Data();
1709
-
1710
1705
  // NOTE: we immediately get a safe reference to the `Napi::External`
1711
1706
  // so that Node will not garbage collect it and the `py::object*` we
1712
1707
  // get out of it will remain valid.
@@ -1718,6 +1713,10 @@ Napi::Value Future_await(const Napi::CallbackInfo& info) {
1718
1713
  py::object* py_context =
1719
1714
  js_external_context.Value(info.Env()).Data();
1720
1715
 
1716
+ std::string rbt_module = js_args.Get("rbtModule").As<Napi::String>();
1717
+
1718
+ std::string state_name = js_args.Get("stateName").As<Napi::String>();
1719
+
1721
1720
  std::string method = js_args.Get("method").As<Napi::String>();
1722
1721
 
1723
1722
  std::string json_task_id = js_args.Get("jsonTaskId").As<Napi::String>();
@@ -1727,9 +1726,9 @@ Napi::Value Future_await(const Napi::CallbackInfo& info) {
1727
1726
  auto promise = deferred->Promise();
1728
1727
 
1729
1728
  adaptor->ScheduleCallbackOnPythonEventLoop(
1730
- [js_external_service, // Ensures `py_service` remains valid.
1731
- py_service,
1732
- method,
1729
+ [rbt_module = std::move(rbt_module),
1730
+ state_name = std::move(state_name),
1731
+ method = std::move(method),
1733
1732
  // Ensures `py_context` remains valid.
1734
1733
  js_external_context,
1735
1734
  py_context,
@@ -1738,12 +1737,16 @@ Napi::Value Future_await(const Napi::CallbackInfo& info) {
1738
1737
  py::object py_task =
1739
1738
  py::module::import("reboot.nodejs.python")
1740
1739
  .attr("create_task_with_context")(
1741
- py_service->attr("_future_await")(
1742
- method,
1743
- py_context,
1744
- json_task_id),
1740
+ py::module::import("reboot.nodejs.python")
1741
+ .attr("task_await")(
1742
+ py_context,
1743
+ py::module::import(rbt_module.c_str())
1744
+ .attr(state_name.c_str()),
1745
+ method,
1746
+ json_task_id),
1745
1747
  py_context,
1746
- "name"_a = ("servicer._future_await(\""
1748
+ "name"_a = ("reboot.nodejs.python.task_await(\""
1749
+ + state_name + "\", \""
1747
1750
  + method + "\", ...) in nodejs")
1748
1751
  .c_str());
1749
1752
  py_task.attr("add_done_callback")(py::cpp_function(
@@ -2200,6 +2203,26 @@ Napi::Value Context_iteration(const Napi::CallbackInfo& info) {
2200
2203
  }
2201
2204
 
2202
2205
 
2206
+ Napi::Value Context_cookie(const Napi::CallbackInfo& info) {
2207
+ Napi::External<py::object> js_external_context =
2208
+ info[0].As<Napi::External<py::object>>();
2209
+
2210
+ // CHECK(...CheckTypeTag(...));
2211
+
2212
+ py::object* py_context = js_external_context.Data();
2213
+
2214
+ std::promise<std::string> promise;
2215
+
2216
+ adaptor->ScheduleCallbackOnPythonEventLoop(
2217
+ [py_context, &promise]() {
2218
+ py::str cookie = py_context->attr("cookie");
2219
+ promise.set_value(std::string(cookie));
2220
+ });
2221
+
2222
+ return Napi::String::New(info.Env(), promise.get_future().get());
2223
+ }
2224
+
2225
+
2203
2226
  Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
2204
2227
  // NOTE: we immediately get a safe reference to the `Napi::External`
2205
2228
  // so that Node will not garbage collect it and the `py::object*` we
@@ -2874,6 +2897,10 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
2874
2897
  Napi::String::New(env, "Context_iteration"),
2875
2898
  Napi::Function::New<Context_iteration>(env));
2876
2899
 
2900
+ exports.Set(
2901
+ Napi::String::New(env, "Context_cookie"),
2902
+ Napi::Function::New<Context_cookie>(env));
2903
+
2877
2904
  exports.Set(
2878
2905
  Napi::String::New(env, "Context_generateIdempotentStateId"),
2879
2906
  Napi::Function::New<Context_generateIdempotentStateId>(env));
@@ -2891,8 +2918,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
2891
2918
  Napi::Function::New<atLeastOrMostOnce>(env));
2892
2919
 
2893
2920
  exports.Set(
2894
- Napi::String::New(env, "Future_await"),
2895
- Napi::Function::New<Future_await>(env));
2921
+ Napi::String::New(env, "Task_await"),
2922
+ Napi::Function::New<Task_await>(env));
2896
2923
 
2897
2924
  exports.Set(
2898
2925
  Napi::String::New(env, "ExternalContext_constructor"),
package/reboot_native.cjs CHANGED
@@ -52,7 +52,7 @@ process.dlopen(
52
52
  exports.python3Path = reboot_native.exports.python3Path;
53
53
  exports.Service_constructor = reboot_native.exports.Service_constructor;
54
54
  exports.Service_call = reboot_native.exports.Service_call;
55
- exports.Future_await = reboot_native.exports.Future_await;
55
+ exports.Task_await = reboot_native.exports.Task_await;
56
56
  exports.ExternalContext_constructor =
57
57
  reboot_native.exports.ExternalContext_constructor;
58
58
  exports.Application_constructor = reboot_native.exports.Application_constructor;
@@ -67,6 +67,7 @@ exports.Reboot_down = reboot_native.exports.Reboot_down;
67
67
  exports.Reboot_url = reboot_native.exports.Reboot_url;
68
68
  exports.Context_auth = reboot_native.exports.Context_auth;
69
69
  exports.Context_stateId = reboot_native.exports.Context_stateId;
70
+ exports.Context_cookie = reboot_native.exports.Context_cookie;
70
71
  exports.Context_iteration = reboot_native.exports.Context_iteration;
71
72
  exports.Context_generateIdempotentStateId =
72
73
  reboot_native.exports.Context_generateIdempotentStateId;
@@ -31,9 +31,10 @@ interface Service_callProps {
31
31
  jsonRequest: string;
32
32
  }
33
33
 
34
- interface Future_awaitProps {
35
- external: NapiExternal;
34
+ interface Task_awaitProps {
36
35
  context: Context | ExternalContext;
36
+ rbtModule: string;
37
+ stateName: string;
37
38
  method: string;
38
39
  jsonTaskId: string;
39
40
  }
@@ -41,7 +42,7 @@ interface Future_awaitProps {
41
42
  export namespace rbt_native {
42
43
  function Service_constructor(props: Service_constructorProps): NapiExternal;
43
44
  function Service_call(props: Service_callProps): string;
44
- function Future_await(props: Future_awaitProps): string;
45
+ function Task_await(props: Task_awaitProps): string;
45
46
  function ExternalContext_constructor(
46
47
  name: string,
47
48
  url: string,
@@ -0,0 +1,37 @@
1
+ export declare const ENVVAR_RBT_SECRETS_DIRECTORY = "RBT_SECRETS_DIRECTORY";
2
+ declare abstract class SecretSource {
3
+ abstract get(secretName: string): Promise<Buffer>;
4
+ }
5
+ export declare class Secrets {
6
+ private static _staticSecretSource;
7
+ private _secretCache;
8
+ private _secretSource;
9
+ constructor();
10
+ static setSecretSource(secretSource: SecretSource | null): void;
11
+ get secretSource(): SecretSource;
12
+ get(secretName: string, { ttlSecs }?: {
13
+ ttlSecs?: number;
14
+ }): Promise<Buffer>;
15
+ }
16
+ export declare class SecretNotFoundException extends Error {
17
+ constructor(message: string);
18
+ }
19
+ export declare class DirectorySecretSource extends SecretSource {
20
+ directory: string;
21
+ constructor(directory: string);
22
+ get(secretName: string): Promise<Buffer>;
23
+ }
24
+ export declare class EnvironmentSecretSource extends SecretSource {
25
+ ENVIRONMENT_VARIABLE_PREFIX: string;
26
+ get(secretName: string): Promise<Buffer>;
27
+ }
28
+ export declare class MockSecretSource extends SecretSource {
29
+ secrets: {
30
+ [key: string]: Buffer;
31
+ };
32
+ constructor(secrets: {
33
+ [key: string]: Buffer;
34
+ });
35
+ get(secretName: string): Promise<Buffer>;
36
+ }
37
+ export {};
@@ -0,0 +1,96 @@
1
+ import * as path from "path";
2
+ import { promises as fs } from "fs";
3
+ export const ENVVAR_RBT_SECRETS_DIRECTORY = "RBT_SECRETS_DIRECTORY";
4
+ class SecretSource {
5
+ }
6
+ export class Secrets {
7
+ constructor() {
8
+ this._secretCache = {};
9
+ if (Secrets._staticSecretSource) {
10
+ this._secretSource = Secrets._staticSecretSource;
11
+ return;
12
+ }
13
+ const secretsDirectory = process.env[ENVVAR_RBT_SECRETS_DIRECTORY];
14
+ if (secretsDirectory) {
15
+ this._secretSource = new DirectorySecretSource(path.resolve(secretsDirectory));
16
+ }
17
+ else {
18
+ this._secretSource = new EnvironmentSecretSource();
19
+ }
20
+ }
21
+ static setSecretSource(secretSource) {
22
+ Secrets._staticSecretSource = secretSource;
23
+ }
24
+ get secretSource() {
25
+ return this._secretSource;
26
+ }
27
+ async get(secretName, { ttlSecs = 15.0 } = {}) {
28
+ const now = Date.now();
29
+ const cachedSecret = this._secretCache[secretName];
30
+ if (cachedSecret && cachedSecret.cachedAt + ttlSecs > now) {
31
+ return cachedSecret.value;
32
+ }
33
+ const value = await this._secretSource.get(secretName);
34
+ this._secretCache[secretName] = new _CachedSecret(value, now);
35
+ return value;
36
+ }
37
+ }
38
+ Secrets._staticSecretSource = null;
39
+ export class SecretNotFoundException extends Error {
40
+ constructor(message) {
41
+ super(message);
42
+ this.name = "SecretNotFoundException";
43
+ }
44
+ }
45
+ export class DirectorySecretSource extends SecretSource {
46
+ constructor(directory) {
47
+ super();
48
+ this.directory = directory;
49
+ }
50
+ async get(secretName) {
51
+ const secretPath = path.join(this.directory, secretName);
52
+ try {
53
+ return await fs.readFile(secretPath);
54
+ }
55
+ catch (error) {
56
+ if (error.code === "ENOENT") {
57
+ throw new SecretNotFoundException(`No secret is stored for secretName=${secretName} (at \`${secretPath}\`).`);
58
+ }
59
+ throw error;
60
+ }
61
+ }
62
+ }
63
+ export class EnvironmentSecretSource extends SecretSource {
64
+ constructor() {
65
+ super(...arguments);
66
+ this.ENVIRONMENT_VARIABLE_PREFIX = "RBT_SECRET_";
67
+ }
68
+ async get(secretName) {
69
+ const environmentVariableName = `${this.ENVIRONMENT_VARIABLE_PREFIX}${secretName.toUpperCase().replace(/-/g, "_")}`;
70
+ const value = process.env[environmentVariableName];
71
+ if (value === undefined) {
72
+ throw new SecretNotFoundException(`No environment variable was set for secretName=${secretName}; ` +
73
+ `expected \`${environmentVariableName}\` to be set`);
74
+ }
75
+ return Buffer.from(value);
76
+ }
77
+ }
78
+ export class MockSecretSource extends SecretSource {
79
+ constructor(secrets) {
80
+ super();
81
+ this.secrets = secrets;
82
+ }
83
+ async get(secretName) {
84
+ const value = this.secrets[secretName];
85
+ if (value === undefined) {
86
+ throw new SecretNotFoundException(`No mock secret was stored for secretName=${secretName}.`);
87
+ }
88
+ return value;
89
+ }
90
+ }
91
+ class _CachedSecret {
92
+ constructor(value, cachedAt) {
93
+ this.value = value;
94
+ this.cachedAt = cachedAt;
95
+ }
96
+ }
package/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const REBOOT_VERSION = "0.23.0";
1
+ export declare const REBOOT_VERSION = "0.24.0";
package/version.js CHANGED
@@ -1 +1 @@
1
- export const REBOOT_VERSION = "0.23.0";
1
+ export const REBOOT_VERSION = "0.24.0";