sst 2.29.0 → 2.30.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.
@@ -13,6 +13,7 @@ import { useJavaHandler } from "./handlers/java.js";
13
13
  import { usePythonHandler } from "./handlers/python.js";
14
14
  import { useRustHandler } from "./handlers/rust.js";
15
15
  import { lazy } from "../util/lazy.js";
16
+ import { Semaphore } from "../util/semaphore.js";
16
17
  export const useRuntimeHandlers = lazy(() => {
17
18
  const handlers = [
18
19
  useNodeHandler(),
@@ -104,9 +105,12 @@ export const useRuntimeHandlers = lazy(() => {
104
105
  const promise = task();
105
106
  pendingBuilds.set(functionID, promise);
106
107
  Logger.debug("Building function", functionID);
107
- const r = await promise;
108
- pendingBuilds.delete(functionID);
109
- return r;
108
+ try {
109
+ return await promise;
110
+ }
111
+ finally {
112
+ pendingBuilds.delete(functionID);
113
+ }
110
114
  },
111
115
  };
112
116
  return result;
@@ -114,6 +118,7 @@ export const useRuntimeHandlers = lazy(() => {
114
118
  export const useFunctionBuilder = lazy(() => {
115
119
  const artifacts = new Map();
116
120
  const handlers = useRuntimeHandlers();
121
+ const semaphore = new Semaphore(4);
117
122
  const result = {
118
123
  artifact: (functionID) => {
119
124
  if (artifacts.has(functionID))
@@ -121,13 +126,19 @@ export const useFunctionBuilder = lazy(() => {
121
126
  return result.build(functionID);
122
127
  },
123
128
  build: async (functionID) => {
124
- const result = await handlers.build(functionID, "start");
125
- if (!result)
126
- return;
127
- if (result.type === "error")
128
- return;
129
- artifacts.set(functionID, result);
130
- return artifacts.get(functionID);
129
+ const unlock = await semaphore.lock();
130
+ try {
131
+ const result = await handlers.build(functionID, "start");
132
+ if (!result)
133
+ return;
134
+ if (result.type === "error")
135
+ return;
136
+ artifacts.set(functionID, result);
137
+ return artifacts.get(functionID);
138
+ }
139
+ finally {
140
+ unlock();
141
+ }
131
142
  },
132
143
  };
133
144
  const watcher = useWatcher();
package/stacks/build.js CHANGED
@@ -14,7 +14,13 @@ export async function load(input, shallow) {
14
14
  const root = await findAbove(input, "package.json");
15
15
  if (!root)
16
16
  throw new VisibleError("Could not find a package.json file");
17
- const outfile = path.join(parsed.dir, `.${parsed.name}.${Date.now()}.mjs`);
17
+ // Create the output file in the current working directory. This is because
18
+ // the sst cli command might not always run from where `sst.config.ts` is. When
19
+ // running from another workspace (ie. running `sst bind` in frontend workspace),
20
+ // the "sst" package imported from within the output file might resolve to a
21
+ // different "sst" package than the one resolved by the sst cli command. This
22
+ // will cause issues like "Project not initialized".
23
+ const outfile = path.join(process.cwd(), `.${parsed.name}.${Date.now()}.mjs`);
18
24
  const pkg = JSON.parse(await fs.readFile(path.join(root, "package.json")).then((x) => x.toString()));
19
25
  try {
20
26
  // Logger.debug("running esbuild on", input);
package/stacks/synth.js CHANGED
@@ -5,68 +5,76 @@ import * as contextproviders from "sst-aws-cdk/lib/context-providers/index.js";
5
5
  import path from "path";
6
6
  import { VisibleError } from "../error.js";
7
7
  import fs from "fs/promises";
8
+ import { Semaphore } from "../util/semaphore.js";
9
+ const sem = new Semaphore(1);
8
10
  export async function synth(opts) {
9
11
  Logger.debug("Synthesizing stacks...");
10
12
  const { App } = await import("../constructs/App.js");
11
13
  const cxapi = await import("@aws-cdk/cx-api");
12
14
  const { Configuration } = await import("sst-aws-cdk/lib/settings.js");
13
15
  const project = useProject();
14
- const identity = await useSTSIdentity();
15
- opts = {
16
- ...opts,
17
- buildDir: opts.buildDir || path.join(project.paths.out, "dist"),
18
- };
19
- await fs.rm(opts.buildDir, { recursive: true, force: true });
20
- await fs.mkdir(opts.buildDir, { recursive: true });
21
- /*
22
- console.log(JSON.stringify(cfg.context));
23
- const executable = new CloudExecutable({
24
- sdkProvider: await useAWSProvider(),
25
- configuration: cfg,
26
- synthesizer: async () => app.synth() as any
27
- });
28
- const { assembly } = await executable.synthesize(true);
29
- */
30
- const cfg = new Configuration();
31
- await cfg.load();
32
- let previous = new Set();
33
- while (true) {
34
- const app = new App({
35
- account: identity.Account,
36
- stage: project.config.stage,
37
- name: project.config.name,
38
- region: project.config.region,
39
- mode: opts.mode,
40
- debugIncreaseTimeout: opts.increaseTimeout,
41
- debugScriptVersion: opts.scriptVersion,
42
- isActiveStack: opts.isActiveStack,
43
- }, {
44
- outdir: opts.buildDir,
45
- context: {
46
- ...cfg.context.all,
47
- [cxapi.PATH_METADATA_ENABLE_CONTEXT]: project.config.cdk?.pathMetadata ?? false,
48
- },
49
- });
50
- await opts.fn(app);
51
- await app.finish();
52
- const assembly = app.synth();
53
- Logger.debug(assembly.manifest.missing);
54
- const { missing } = assembly.manifest;
55
- const provider = await useAWSProvider();
56
- if (missing && missing.length) {
57
- const next = missing.map((x) => x.key);
58
- if (next.length === previous.size && next.every((x) => previous.has(x)))
59
- throw new VisibleError(formatErrorMessage(next.join("")));
60
- Logger.debug("Looking up context for:", next, "Previous:", previous);
61
- previous = new Set(next);
62
- await contextproviders.provideContextValues(missing, cfg.context, provider);
63
- if (cfg.context.keys.length) {
64
- await cfg.saveContext();
16
+ const cwd = process.cwd();
17
+ process.chdir(project.paths.root);
18
+ const unlock = await sem.lock();
19
+ try {
20
+ return await synthInRoot();
21
+ }
22
+ catch (e) {
23
+ throw e;
24
+ }
25
+ finally {
26
+ unlock();
27
+ process.chdir(cwd);
28
+ }
29
+ async function synthInRoot() {
30
+ const identity = await useSTSIdentity();
31
+ opts = {
32
+ ...opts,
33
+ buildDir: opts.buildDir || path.join(project.paths.out, "dist"),
34
+ };
35
+ await fs.rm(opts.buildDir, { recursive: true, force: true });
36
+ await fs.mkdir(opts.buildDir, { recursive: true });
37
+ const cfg = new Configuration();
38
+ await cfg.load();
39
+ let previous = new Set();
40
+ while (true) {
41
+ const app = new App({
42
+ account: identity.Account,
43
+ stage: project.config.stage,
44
+ name: project.config.name,
45
+ region: project.config.region,
46
+ mode: opts.mode,
47
+ debugIncreaseTimeout: opts.increaseTimeout,
48
+ debugScriptVersion: opts.scriptVersion,
49
+ isActiveStack: opts.isActiveStack,
50
+ }, {
51
+ outdir: opts.buildDir,
52
+ context: {
53
+ ...cfg.context.all,
54
+ [cxapi.PATH_METADATA_ENABLE_CONTEXT]: project.config.cdk?.pathMetadata ?? false,
55
+ },
56
+ });
57
+ await opts.fn(app);
58
+ await app.finish();
59
+ const assembly = app.synth();
60
+ Logger.debug(assembly.manifest.missing);
61
+ const { missing } = assembly.manifest;
62
+ const provider = await useAWSProvider();
63
+ if (missing && missing.length) {
64
+ const next = missing.map((x) => x.key);
65
+ if (next.length === previous.size && next.every((x) => previous.has(x)))
66
+ throw new VisibleError(formatErrorMessage(next.join("")));
67
+ Logger.debug("Looking up context for:", next, "Previous:", previous);
68
+ previous = new Set(next);
69
+ await contextproviders.provideContextValues(missing, cfg.context, provider);
70
+ if (cfg.context.keys.length) {
71
+ await cfg.saveContext();
72
+ }
73
+ continue;
65
74
  }
66
- continue;
75
+ Logger.debug("Finished synthesizing");
76
+ return assembly;
67
77
  }
68
- Logger.debug("Finished synthesizing");
69
- return assembly;
70
78
  }
71
79
  }
72
80
  function formatErrorMessage(message) {
@@ -0,0 +1,7 @@
1
+ export declare class Semaphore {
2
+ private queue;
3
+ private locked;
4
+ private maxLocks;
5
+ constructor(maxLocks?: number);
6
+ lock(): Promise<() => void>;
7
+ }
@@ -0,0 +1,27 @@
1
+ export class Semaphore {
2
+ queue = [];
3
+ locked = 0;
4
+ maxLocks;
5
+ constructor(maxLocks = 1) {
6
+ this.maxLocks = maxLocks;
7
+ }
8
+ lock() {
9
+ return new Promise((resolve) => {
10
+ const unlock = () => {
11
+ this.locked--;
12
+ const next = this.queue.shift();
13
+ if (next) {
14
+ this.locked++;
15
+ next(unlock);
16
+ }
17
+ };
18
+ if (this.locked < this.maxLocks) {
19
+ this.locked++;
20
+ resolve(unlock);
21
+ }
22
+ else {
23
+ this.queue.push(unlock);
24
+ }
25
+ });
26
+ }
27
+ }