sst 2.36.8 → 2.37.1

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.
@@ -30,26 +30,32 @@ export class AstroSite extends SsrSite {
30
30
  static getCFRoutingFunction({ routes, pageResolution, }) {
31
31
  const serializedRoutes = routes.map((route) => ({
32
32
  rt: route.route,
33
- pt: route.pattern,
34
- t: route.type,
33
+ pt: new RegExp(route.pattern),
34
+ t: route.type[1],
35
35
  pr: route.prerender === true ? true : undefined,
36
36
  rp: route.redirectPath,
37
37
  rs: route.redirectStatus,
38
38
  }));
39
+ function objectToString(obj) {
40
+ return `{ ${Object.entries(obj)
41
+ .filter(([_, value]) => value !== undefined)
42
+ .map(([key, value]) => `${key}: ${typeof value === "string" ? `'${value}'` : value}`)
43
+ .join(", ")} }`;
44
+ }
39
45
  return `
40
- var routes = ${JSON.stringify(serializedRoutes)}
41
- var match = routes.find((route) => new RegExp(route.pt).test(request.uri));
46
+ var routes = [${serializedRoutes.map(objectToString).join(", ")}]
47
+ var match = routes.find((route) => route.pt.test(request.uri));
42
48
  if (match) {
43
- if (match.t === "redirect") {
49
+ if (match.t === "r") {
44
50
  var redirectPath = match.rp;
45
- new RegExp(match.pt).exec(request.uri)?.forEach((match, index) => {
51
+ (match.pt.exec(request.uri) || []).forEach((match, index) => {
46
52
  redirectPath = redirectPath.replace(\`\\\${\${index}}\`, match)
47
53
  });
48
54
  return {
49
55
  statusCode: match.rs || 308,
50
56
  headers: { location: { value: redirectPath } },
51
57
  };
52
- } else if (match.t === "page" && match.pr) {
58
+ } else if (match.t === "p" && match.pr) {
53
59
  ${pageResolution === "file"
54
60
  ? `request.uri = request.uri === "/" ? "/index.html" : request.uri.replace(/\\/?$/, ".html");`
55
61
  : `request.uri = request.uri.replace(/\\/?$/, "/index.html");`}
@@ -125,6 +131,10 @@ export class AstroSite extends SsrSite {
125
131
  });
126
132
  }
127
133
  else {
134
+ plan.cloudFrontFunctions.imageServiceCfFunction = {
135
+ constructId: "ImageServiceCloudFrontFunction",
136
+ injections: [this.useCloudFrontFunctionHostHeaderInjection()],
137
+ };
128
138
  plan.origins.regionalServer = {
129
139
  type: "function",
130
140
  constructId: "ServerFunction",
@@ -146,6 +156,12 @@ export class AstroSite extends SsrSite {
146
156
  cacheType: "static",
147
157
  pattern: `${buildMeta.clientBuildVersionedSubDir}/*`,
148
158
  origin: "staticsServer",
159
+ }, {
160
+ cacheType: "server",
161
+ pattern: "_image",
162
+ cfFunction: "imageServiceCfFunction",
163
+ origin: "regionalServer",
164
+ allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
149
165
  }, ...buildMeta.serverRoutes?.map((route) => ({
150
166
  cacheType: "server",
151
167
  pattern: route,
@@ -8,6 +8,7 @@ import { Stack } from "./Stack.js";
8
8
  import { isCDKConstruct } from "./Construct.js";
9
9
  import { HttpsRedirect } from "./cdk/website-redirect.js";
10
10
  import { DnsValidatedCertificate } from "./cdk/dns-validated-certificate.js";
11
+ import { VisibleError } from "../error.js";
11
12
  export class Distribution extends Construct {
12
13
  scope;
13
14
  props;
@@ -108,10 +109,10 @@ export class Distribution extends Construct {
108
109
  if (!cdk?.distribution)
109
110
  return;
110
111
  if (cdk.distribution.certificate) {
111
- throw new Error(`Do not configure the "cfDistribution.certificate". Use the "customDomain" to configure the domain certificate.`);
112
+ throw new VisibleError(`Do not configure the "cfDistribution.certificate". Use the "customDomain" to configure the domain certificate.`);
112
113
  }
113
114
  if (cdk.distribution.domainNames) {
114
- throw new Error(`Do not configure the "cfDistribution.domainNames". Use the "customDomain" to configure the domain name.`);
115
+ throw new VisibleError(`Do not configure the "cfDistribution.domainNames". Use the "customDomain" to configure the domain name.`);
115
116
  }
116
117
  }
117
118
  validateCustomDomainSettings() {
@@ -122,15 +123,18 @@ export class Distribution extends Construct {
122
123
  if (typeof customDomain === "string") {
123
124
  return;
124
125
  }
126
+ if (!customDomain.domainName) {
127
+ throw new VisibleError(`Missing "domainName" for customDomain.`);
128
+ }
125
129
  if (customDomain.isExternalDomain === true) {
126
130
  if (!customDomain.cdk?.certificate) {
127
- throw new Error(`A valid certificate is required when "isExternalDomain" is set to "true".`);
131
+ throw new VisibleError(`A valid certificate is required when "isExternalDomain" is set to "true".`);
128
132
  }
129
133
  if (customDomain.domainAlias) {
130
- throw new Error(`Domain alias is only supported for domains hosted on Amazon Route 53. Do not set the "customDomain.domainAlias" when "isExternalDomain" is enabled.`);
134
+ throw new VisibleError(`Domain alias is only supported for domains hosted on Amazon Route 53. Do not set the "customDomain.domainAlias" when "isExternalDomain" is enabled.`);
131
135
  }
132
136
  if (customDomain.hostedZone) {
133
- throw new Error(`Hosted zones can only be configured for domains hosted on Amazon Route 53. Do not set the "customDomain.hostedZone" when "isExternalDomain" is enabled.`);
137
+ throw new VisibleError(`Hosted zones can only be configured for domains hosted on Amazon Route 53. Do not set the "customDomain.hostedZone" when "isExternalDomain" is enabled.`);
134
138
  }
135
139
  }
136
140
  }
@@ -223,7 +227,7 @@ export class Distribution extends Construct {
223
227
  domainNames.push(customDomain.domainName);
224
228
  if (customDomain.alternateNames) {
225
229
  if (!customDomain.cdk?.certificate)
226
- throw new Error("Certificates for alternate domains cannot be automatically created. Please specify certificate to use");
230
+ throw new VisibleError("Certificates for alternate domains cannot be automatically created. Please specify certificate to use");
227
231
  domainNames.push(...customDomain.alternateNames);
228
232
  }
229
233
  }
@@ -99,6 +99,7 @@ export class EdgeFunction extends Construct {
99
99
  const { nodejs } = this.props;
100
100
  useFunctions().add(this.node.addr, {
101
101
  ...this.props,
102
+ architecture: "x86_64",
102
103
  nodejs: {
103
104
  ...nodejs,
104
105
  banner: [
@@ -686,6 +686,8 @@ export declare class Function extends CDKFunction implements SSTConstruct {
686
686
  readonly _isLiveDevEnabled: boolean;
687
687
  /** @internal */
688
688
  readonly _doNotAllowOthersToBind?: boolean;
689
+ /** @internal */
690
+ _overrideMetadataHandler?: string;
689
691
  private missingSourcemap?;
690
692
  private functionUrl?;
691
693
  private props;
@@ -67,6 +67,8 @@ export class Function extends CDKFunction {
67
67
  _isLiveDevEnabled;
68
68
  /** @internal */
69
69
  _doNotAllowOthersToBind;
70
+ /** @internal */
71
+ _overrideMetadataHandler;
70
72
  missingSourcemap;
71
73
  functionUrl;
72
74
  props;
@@ -371,7 +373,7 @@ export class Function extends CDKFunction {
371
373
  data: {
372
374
  arn: this.functionArn,
373
375
  runtime: this.props.runtime,
374
- handler: this.props.handler,
376
+ handler: this._overrideMetadataHandler ?? this.props.handler,
375
377
  missingSourcemap: this.missingSourcemap === true ? true : undefined,
376
378
  localId: this.node.addr,
377
379
  secrets: this.allBindings
package/constructs/RDS.js CHANGED
@@ -312,6 +312,8 @@ export class RDS extends Construct {
312
312
  },
313
313
  _doNotAllowOthersToBind: true,
314
314
  });
315
+ this.migratorFunction._overrideMetadataHandler =
316
+ "rds-migrator/index.handler";
315
317
  }
316
318
  createMigrationCustomResource(migrations) {
317
319
  const app = this.node.root;
@@ -357,6 +359,7 @@ export class RDS extends Construct {
357
359
  nodir: true,
358
360
  follow: true,
359
361
  cwd: migrations,
362
+ ignore: ["**/node_modules/**"],
360
363
  });
361
364
  // Calculate hash of all files content
362
365
  return crypto
@@ -65,6 +65,17 @@ export interface ServiceProps {
65
65
  *```
66
66
  */
67
67
  memory?: `${number} GB`;
68
+ /**
69
+ * The amount of ephemeral storage allocated, in GB.
70
+ * @default "20 GB"
71
+ * @example
72
+ * ```js
73
+ * {
74
+ * storage: "100 GB",
75
+ * }
76
+ * ```
77
+ */
78
+ storage?: `${number} GB`;
68
79
  /**
69
80
  * The port number on the container.
70
81
  * @default 3000
@@ -373,6 +384,7 @@ type ServiceNormalizedProps = ServiceProps & {
373
384
  cpu: Exclude<ServiceProps["cpu"], undefined>;
374
385
  path: Exclude<ServiceProps["path"], undefined>;
375
386
  memory: Exclude<ServiceProps["memory"], undefined>;
387
+ storage: Exclude<ServiceProps["storage"], undefined>;
376
388
  port: Exclude<ServiceProps["port"], undefined>;
377
389
  logRetention: Exclude<ServiceProps["logRetention"], undefined>;
378
390
  };
@@ -468,7 +480,7 @@ export declare class Service extends Construct implements SSTConstruct {
468
480
  */
469
481
  addEnvironment(name: string, value: string): void;
470
482
  private validateServiceExists;
471
- private validateMemoryAndCpu;
483
+ private validateMemoryCpuAndStorage;
472
484
  private createVpc;
473
485
  private createService;
474
486
  private createLoadBalancer;
@@ -18,12 +18,13 @@ import { useDeferredTasks } from "./deferred_task.js";
18
18
  import { attachPermissionsToRole } from "./util/permission.js";
19
19
  import { bindEnvironment, bindPermissions, getParameterPath, getReferencedSecrets, } from "./util/functionBinding.js";
20
20
  import { useProject } from "../project.js";
21
- import { Vpc, } from "aws-cdk-lib/aws-ec2";
21
+ import { Vpc } from "aws-cdk-lib/aws-ec2";
22
22
  import { AwsLogDriver, Cluster, ContainerImage, CpuArchitecture, FargateService, FargateTaskDefinition, } from "aws-cdk-lib/aws-ecs";
23
23
  import { LogGroup, LogRetention, RetentionDays } from "aws-cdk-lib/aws-logs";
24
24
  import { Platform } from "aws-cdk-lib/aws-ecr-assets";
25
25
  import { ApplicationLoadBalancer, } from "aws-cdk-lib/aws-elasticloadbalancingv2";
26
26
  import { createAppContext } from "./context.js";
27
+ import { toCdkSize } from "./index.js";
27
28
  const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
28
29
  const NIXPACKS_IMAGE_NAME = "sst-nixpacks";
29
30
  const supportedCpus = {
@@ -158,6 +159,7 @@ export class Service extends Construct {
158
159
  architecture: props?.architecture || "x86_64",
159
160
  cpu: props?.cpu || "0.25 vCPU",
160
161
  memory: props?.memory || "0.5 GB",
162
+ storage: props?.storage || "20 GB",
161
163
  port: props?.port || 3000,
162
164
  logRetention: props?.logRetention || "infinite",
163
165
  ...props,
@@ -165,7 +167,7 @@ export class Service extends Construct {
165
167
  this.doNotDeploy =
166
168
  !stack.isActive || (app.mode === "dev" && !this.props.dev?.deploy);
167
169
  this.validateServiceExists();
168
- this.validateMemoryAndCpu();
170
+ this.validateMemoryCpuAndStorage();
169
171
  useServices().add(stack.stackName, id, this.props);
170
172
  if (this.doNotDeploy) {
171
173
  this.devFunction = this.createDevFunction();
@@ -376,8 +378,8 @@ export class Service extends Construct {
376
378
  }
377
379
  }
378
380
  }
379
- validateMemoryAndCpu() {
380
- const { memory, cpu } = this.props;
381
+ validateMemoryCpuAndStorage() {
382
+ const { memory, cpu, storage } = this.props;
381
383
  if (!supportedCpus[cpu]) {
382
384
  throw new VisibleError(`In the "${this.node.id}" Service, only the following "cpu" settings are supported: ${Object.keys(supportedCpus).join(", ")}`);
383
385
  }
@@ -385,6 +387,10 @@ export class Service extends Construct {
385
387
  if (!supportedMemories[cpu][memory]) {
386
388
  throw new VisibleError(`In the "${this.node.id}" Service, only the following "memory" settings are supported with "${cpu}": ${Object.keys(supportedMemories[cpu]).join(", ")}`);
387
389
  }
390
+ const storageInGiB = toCdkSize(storage).toGibibytes();
391
+ if (storageInGiB < 20 || storageInGiB > 200) {
392
+ throw new VisibleError(`In the "${this.node.id}" Service, the supported value for storage is between "20 GB" and "200 GB"`);
393
+ }
388
394
  }
389
395
  createVpc() {
390
396
  const { cdk } = this.props;
@@ -394,7 +400,7 @@ export class Service extends Construct {
394
400
  }));
395
401
  }
396
402
  createService(vpc) {
397
- const { architecture, cpu, memory, port, logRetention, cdk } = this.props;
403
+ const { architecture, cpu, memory, storage, port, logRetention, cdk } = this.props;
398
404
  const app = this.node.root;
399
405
  const clusterName = app.logicalPrefixedName(this.node.id);
400
406
  const logGroup = new LogRetention(this, "LogRetention", {
@@ -408,10 +414,14 @@ export class Service extends Construct {
408
414
  clusterName,
409
415
  vpc,
410
416
  });
417
+ const ephemeralStorageGiB = toCdkSize(storage).toGibibytes();
411
418
  const taskDefinition = new FargateTaskDefinition(this, `TaskDefinition`, {
412
- // @ts-ignore
419
+ // @ts-expect-error
413
420
  memoryLimitMiB: supportedMemories[cpu][memory],
414
421
  cpu: supportedCpus[cpu],
422
+ // note: the minimum allowed value by CloudFormation is 21, set to
423
+ // undefined to set to the default value of 20
424
+ ephemeralStorageGiB: ephemeralStorageGiB === 20 ? undefined : ephemeralStorageGiB,
415
425
  runtimePlatform: {
416
426
  cpuArchitecture: architecture === "arm64"
417
427
  ? CpuArchitecture.ARM64
@@ -40,6 +40,7 @@ export class SsrFunction extends Construct {
40
40
  memorySize: 1024,
41
41
  streaming: false,
42
42
  injections: [],
43
+ architecture: Architecture.ARM_64,
43
44
  ...props,
44
45
  environment: props.environment || {},
45
46
  permissions: props.permissions || [],
@@ -104,7 +105,7 @@ export class SsrFunction extends Construct {
104
105
  : runtime === "nodejs16.x"
105
106
  ? Runtime.NODEJS_16_X
106
107
  : Runtime.NODEJS_18_X,
107
- architecture: architecture || Architecture.ARM_64,
108
+ architecture,
108
109
  memorySize: typeof memorySize === "string"
109
110
  ? toCdkSize(memorySize).toMebibytes()
110
111
  : memorySize,
@@ -173,11 +174,13 @@ export class SsrFunction extends Construct {
173
174
  });
174
175
  }
175
176
  async buildAssetFromHandler() {
176
- const { handler, runtime, nodejs, copyFiles, injections } = this.props;
177
+ const { handler, runtime, nodejs, copyFiles, architecture: enumArchitecture, } = this.props;
178
+ const architecture = enumArchitecture === Architecture.X86_64 ? "x86_64" : "arm_64";
177
179
  useFunctions().add(this.node.addr, {
178
180
  handler,
179
181
  runtime,
180
182
  nodejs,
183
+ architecture,
181
184
  copyFiles,
182
185
  });
183
186
  // build function
@@ -117,6 +117,10 @@ export class Auth extends Construct {
117
117
  type: "plain",
118
118
  value: this.url,
119
119
  },
120
+ authId: {
121
+ type: "auth_id",
122
+ value: this.id,
123
+ },
120
124
  },
121
125
  permissions: {},
122
126
  };
@@ -14,6 +14,9 @@ export interface FunctionBindingProps {
14
14
  } | {
15
15
  type: "site_url";
16
16
  value: string;
17
+ } | {
18
+ type: "auth_id";
19
+ value: string;
17
20
  }>;
18
21
  }
19
22
  export declare function bindEnvironment(c: SSTConstruct): Record<string, string>;
@@ -15,6 +15,9 @@ export function bindEnvironment(c) {
15
15
  else if (variable.type === "secret_reference") {
16
16
  environment[envName] = placeholderSecretReferenceValue(variable.secret);
17
17
  }
18
+ else if (variable.type === "auth_id") {
19
+ environment["AUTH_ID"] = variable.value;
20
+ }
18
21
  });
19
22
  }
20
23
  return environment;
@@ -1,2 +1,2 @@
1
- export * from "./context.js";
1
+ export { Context } from "./context2.js";
2
2
  export * from "./handler.js";
package/context/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./context.js";
1
+ export { Context } from "./context2.js";
2
2
  export * from "./handler.js";
@@ -0,0 +1,16 @@
1
+ import { OauthConfig } from "./oauth.js";
2
+ export declare const AppleAdapter: (config: OauthConfig) => () => Promise<{
3
+ type: "success";
4
+ properties: {
5
+ tokenset: import("openid-client").TokenSet;
6
+ client: import("openid-client").BaseClient;
7
+ };
8
+ } | {
9
+ type: "step";
10
+ properties: {
11
+ statusCode: number;
12
+ headers: {
13
+ location: string;
14
+ };
15
+ };
16
+ } | undefined>;
@@ -0,0 +1,80 @@
1
+ import { generators, Issuer } from "openid-client";
2
+ import { useBody, useCookie, useDomainName, usePathParam, useResponse, } from "../../../api/index.js";
3
+ const querystring = require("querystring");
4
+ // This adapter support the OAuth flow with the response_mode "form_post" for now.
5
+ // More details about the flow:
6
+ // https://developer.apple.com/documentation/devicemanagement/user_enrollment/onboarding_users_with_account_sign-in/implementing_the_oauth2_authentication_user-enrollment_flow
7
+ //
8
+ // Also note that Apple's discover uri does not work for the OAuth flow, as the
9
+ // userinfo_endpoint are not included in the response.
10
+ // await Issuer.discover("https://appleid.apple.com/.well-known/openid-configuration/");
11
+ const issuer = await Issuer.discover("https://appleid.apple.com/.well-known/openid-configuration");
12
+ export const AppleAdapter =
13
+ /* @__PURE__ */
14
+ (config) => {
15
+ return async function () {
16
+ const step = usePathParam("step");
17
+ const callback = "https://" + useDomainName() + "/callback";
18
+ console.log("callback", callback);
19
+ const client = new issuer.Client({
20
+ client_id: config.clientID,
21
+ client_secret: config.clientSecret,
22
+ redirect_uris: [callback],
23
+ response_types: ["code"],
24
+ });
25
+ if (step === "authorize" || step === "connect") {
26
+ const code_verifier = generators.codeVerifier();
27
+ const state = generators.state();
28
+ const code_challenge = generators.codeChallenge(code_verifier);
29
+ const url = client.authorizationUrl({
30
+ scope: config.scope,
31
+ code_challenge: code_challenge,
32
+ code_challenge_method: "S256",
33
+ state,
34
+ prompt: config.prompt,
35
+ ...config.params,
36
+ });
37
+ useResponse().cookies({
38
+ auth_code_verifier: code_verifier,
39
+ auth_state: state,
40
+ }, {
41
+ httpOnly: true,
42
+ secure: true,
43
+ maxAge: 60 * 10,
44
+ sameSite: "None",
45
+ });
46
+ return {
47
+ type: "step",
48
+ properties: {
49
+ statusCode: 302,
50
+ headers: {
51
+ location: url,
52
+ },
53
+ },
54
+ };
55
+ }
56
+ if (step === "callback") {
57
+ let params;
58
+ if (config &&
59
+ config.params &&
60
+ config.params.response_mode === "form_post") {
61
+ const body = useBody();
62
+ params = querystring.parse(body);
63
+ }
64
+ const code_verifier = useCookie("auth_code_verifier");
65
+ const state = useCookie("auth_state");
66
+ const tokenset = await client["callback"](callback, params, {
67
+ code_verifier,
68
+ state,
69
+ });
70
+ const x = {
71
+ type: "success",
72
+ properties: {
73
+ tokenset,
74
+ client,
75
+ },
76
+ };
77
+ return x;
78
+ }
79
+ };
80
+ };
@@ -10,6 +10,7 @@ export * from "./adapter/microsoft.js";
10
10
  export * from "./adapter/oauth.js";
11
11
  export * from "./adapter/spotify.js";
12
12
  export * from "./adapter/code.js";
13
+ export * from "./adapter/apple.js";
13
14
  export type { Adapter } from "./adapter/adapter.js";
14
15
  export * from "./session.js";
15
16
  export * from "./handler.js";
@@ -9,6 +9,7 @@ export * from "./adapter/microsoft.js";
9
9
  export * from "./adapter/oauth.js";
10
10
  export * from "./adapter/spotify.js";
11
11
  export * from "./adapter/code.js";
12
+ export * from "./adapter/apple.js";
12
13
  export * from "./session.js";
13
14
  export * from "./handler.js";
14
15
  export * from "./encryption.js";
@@ -9,7 +9,7 @@ export declare function GraphQLHandler<UserContext extends {}>(options: YogaServ
9
9
  headers: {
10
10
  [k: string]: string;
11
11
  };
12
- body: any;
12
+ body: string;
13
13
  isBase64Encoded: boolean;
14
14
  }>;
15
15
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.36.8",
4
+ "version": "2.37.1",
5
5
  "bin": {
6
6
  "sst": "cli/sst.js"
7
7
  },
@@ -120,7 +120,7 @@
120
120
  "@types/ws": "^8.5.3",
121
121
  "@types/yargs": "^17.0.13",
122
122
  "archiver": "^5.3.1",
123
- "astro-sst": "2.36.8",
123
+ "astro-sst": "2.37.1",
124
124
  "async": "^3.2.4",
125
125
  "tsx": "^3.12.1",
126
126
  "typescript": "^5.2.2",
@@ -154593,7 +154593,8 @@ var handler = (event) => {
154593
154593
  return event.RequestType ? customResourceEventHandler(event) : lambdaEventHandler(event);
154594
154594
  };
154595
154595
  var customResourceEventHandler = wrapper(async (cfnRequest) => {
154596
- log("Handling custom resource event", cfnRequest);
154596
+ const { ResponseURL, ...other } = cfnRequest;
154597
+ log("Handling custom resource event", other);
154597
154598
  switch (cfnRequest.ResourceType) {
154598
154599
  case "Custom::AuthKeys":
154599
154600
  await AuthKeys(cfnRequest);
@@ -1,13 +0,0 @@
1
- export declare const Context: {
2
- create: typeof create;
3
- reset: typeof reset;
4
- memo: typeof memo;
5
- };
6
- declare function create<C>(cb?: (() => C) | string, name?: string): {
7
- use(): C;
8
- reset(): void;
9
- provide(value: C): void;
10
- };
11
- declare function reset(): void;
12
- export declare function memo<C>(cb: () => C, name?: string): () => C;
13
- export {};
@@ -1,69 +0,0 @@
1
- export const Context = {
2
- create,
3
- reset,
4
- memo,
5
- };
6
- const state = {
7
- requestID: "",
8
- contexts: new Map(),
9
- tracking: [],
10
- };
11
- function create(cb, name) {
12
- const id = typeof cb === "string" ? cb : name || Symbol(cb?.toString());
13
- return {
14
- use() {
15
- let result = state.contexts.get(id);
16
- if (!result) {
17
- if (!cb || typeof cb === "string")
18
- throw new Error(`"${String(id)}" context was not provided.`);
19
- state.tracking.push(id);
20
- const value = cb();
21
- state.tracking.pop();
22
- result = {
23
- value,
24
- dependants: new Set(),
25
- };
26
- state.contexts.set(id, result);
27
- }
28
- const last = state.tracking[state.tracking.length - 1];
29
- // Use is being called within another context booting up so mark it as a dependent
30
- if (last)
31
- result.dependants.add(last);
32
- return result.value;
33
- },
34
- reset() {
35
- resetDependencies(id);
36
- state.contexts.delete(id);
37
- },
38
- provide(value) {
39
- // If a new request has started, automatically clear all contexts
40
- const requestID = global[Symbol.for("aws.lambda.runtime.requestId")];
41
- if (state.requestID !== requestID) {
42
- state.requestID = requestID;
43
- reset();
44
- }
45
- // If the context is already set, we need to reset its dependants
46
- resetDependencies(id);
47
- state.contexts.set(id, {
48
- value,
49
- dependants: new Set(),
50
- });
51
- },
52
- };
53
- }
54
- function reset() {
55
- state.contexts.clear();
56
- }
57
- function resetDependencies(id) {
58
- const info = state.contexts.get(id);
59
- if (!info)
60
- return;
61
- for (const dependantID of info.dependants) {
62
- state.contexts.delete(dependantID);
63
- resetDependencies(dependantID);
64
- }
65
- }
66
- export function memo(cb, name) {
67
- const ctx = create(cb, name);
68
- return ctx.use;
69
- }