sst 3.0.13 → 3.0.15

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.
@@ -8,7 +8,7 @@ export const OauthAdapter =
8
8
  (config) => {
9
9
  return async function (routes, ctx) {
10
10
  function getClient(c) {
11
- const callback = c.req.url.replace(/authorize$/, "callback");
11
+ const callback = c.req.url.replace(/authorize\/.*$/, "callback");
12
12
  return [
13
13
  callback,
14
14
  new config.issuer.Client({
@@ -3,7 +3,7 @@ import { getCookie } from "hono/cookie";
3
3
  export const OidcAdapter = /* @__PURE__ */ (config) => {
4
4
  return async function (routes, ctx) {
5
5
  routes.get("/authorize", async (c) => {
6
- const callback = c.req.url.replace(/authorize$/, "callback");
6
+ const callback = c.req.url.replace(/authorize\/.*$/, "callback");
7
7
  const client = new config.issuer.Client({
8
8
  client_id: config.clientID,
9
9
  redirect_uris: [callback],
@@ -23,7 +23,7 @@ export const OidcAdapter = /* @__PURE__ */ (config) => {
23
23
  return c.redirect(url);
24
24
  });
25
25
  routes.post("/callback", async (c) => {
26
- const callback = c.req.url.replace(/authorize$/, "callback");
26
+ const callback = c.req.url.replace(/authorize\/.*$/, "callback");
27
27
  const client = new config.issuer.Client({
28
28
  client_id: config.clientID,
29
29
  redirect_uris: [callback],
@@ -52,12 +52,20 @@ export function AuthHandler(input) {
52
52
  }
53
53
  const options = {
54
54
  signing: {
55
- privateKey: () => importPKCS8(process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RS512"),
56
- publicKey: () => importSPKI(process.env.AUTH_PUBLIC_KEY || Resource.AUTH_PUBLIC_KEY, "RS512"),
55
+ privateKey: () => importPKCS8(
56
+ // @ts-expect-error
57
+ process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RS512"),
58
+ publicKey: () => importSPKI(
59
+ // @ts-expect-error
60
+ process.env.AUTH_PUBLIC_KEY || Resource.AUTH_PUBLIC_KEY, "RS512"),
57
61
  },
58
62
  encryption: {
59
- privateKey: () => importPKCS8(process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RSA-OAEP-512"),
60
- publicKey: () => importSPKI(process.env.AUTH_PUBLIC_KEY || Resource.AUTH_PUBLIC_KEY, "RSA-OAEP-512"),
63
+ privateKey: () => importPKCS8(
64
+ // @ts-expect-error
65
+ process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RSA-OAEP-512"),
66
+ publicKey: () => importSPKI(
67
+ // @ts-expect-error
68
+ process.env.AUTH_PUBLIC_KEY || Resource.AUTH_PUBLIC_KEY, "RSA-OAEP-512"),
61
69
  },
62
70
  algorithm: "RS512",
63
71
  async success(ctx, properties) {
@@ -121,7 +129,7 @@ export function AuthHandler(input) {
121
129
  });
122
130
  },
123
131
  };
124
- app.get("/token", async (c) => {
132
+ app.post("/token", async (c) => {
125
133
  console.log("token request");
126
134
  const form = await c.req.formData();
127
135
  if (form.get("grant_type") !== "authorization_code") {
@@ -152,6 +160,7 @@ export function AuthHandler(input) {
152
160
  const response_type = c.req.query("response_type") || getCookie(c, "response_type");
153
161
  const redirect_uri = c.req.query("redirect_uri") || getCookie(c, "redirect_uri");
154
162
  const state = c.req.query("state") || getCookie(c, "state");
163
+ const client_id = c.req.query("client_id") || getCookie(c, "client_id");
155
164
  if (!provider) {
156
165
  c.status(400);
157
166
  return c.text("Missing provider");
@@ -164,10 +173,15 @@ export function AuthHandler(input) {
164
173
  c.status(400);
165
174
  return c.text("Missing response_type");
166
175
  }
176
+ if (!client_id) {
177
+ c.status(400);
178
+ return c.text("Missing client_id");
179
+ }
167
180
  options.cookie(c, "provider", provider, 60 * 10);
168
181
  options.cookie(c, "response_type", response_type, 60 * 10);
169
182
  options.cookie(c, "redirect_uri", redirect_uri, 60 * 10);
170
183
  options.cookie(c, "state", state || "", 60 * 10);
184
+ options.cookie(c, "client_id", client_id || "", 60 * 10);
171
185
  if (input.callbacks.auth.start) {
172
186
  await input.callbacks.auth.start(c.req.raw);
173
187
  }
@@ -13,7 +13,9 @@ export function createSessionBuilder() {
13
13
  return result.payload;
14
14
  },
15
15
  async create(session) {
16
- const privateKey = await importPKCS8(process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RS512");
16
+ const privateKey = await importPKCS8(
17
+ // @ts-expect-error
18
+ process.env.AUTH_PRIVATE_KEY || Resource.AUTH_PRIVATE_KEY, "RS512");
17
19
  const token = await new SignJWT(session)
18
20
  .setProtectedHeader({ alg: "RS512" })
19
21
  .setExpirationTime("1yr")
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from "./realtime/index.js";
1
2
  export * from "./resource.js";
2
- export * from "./vector-client.js";
3
+ export * from "./vector/index.js";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
+ export * from "./realtime/index.js";
1
2
  export * from "./resource.js";
2
- export * from "./vector-client.js";
3
+ export * from "./vector/index.js";
@@ -0,0 +1,59 @@
1
+ import type { IoTCustomAuthorizerHandler } from "aws-lambda";
2
+ export interface RealtimeAuthResult {
3
+ /**
4
+ * The topics the client can subscribe to.
5
+ * @example
6
+ * For example, this subscribes to specific topics.
7
+ * ```js
8
+ * {
9
+ * subscribe: ["chat/room1", "chat/room2"]
10
+ * }
11
+ * ```
12
+ *
13
+ * And to subscribe to all topics under a specific prefix.
14
+ * ```js
15
+ * {
16
+ * subscribe: ["chat/#"]
17
+ * }
18
+ * ```
19
+ */
20
+ subscribe?: string[];
21
+ /**
22
+ * The topics the client can publish to.
23
+ * @example
24
+ * For example, this publishes to specific topics.
25
+ * ```js
26
+ * {
27
+ * publish: ["chat/room1", "chat/room2"]
28
+ * }
29
+ * ```
30
+ * And to publish to all topics under a specific prefix.
31
+ * ```js
32
+ * {
33
+ * publish: ["chat/#"]
34
+ * }
35
+ * ```
36
+ */
37
+ publish?: string[];
38
+ }
39
+ /**
40
+ * Creates an authorization handler for the `Realtime` component, that validates
41
+ * the token and grants permissions for the topics the client can subscribe and publish to.
42
+ *
43
+ * @example
44
+ * ```js
45
+ * import { RealtimeAuthHandler, Resource } from "sst";
46
+ *
47
+ * export const handler = RealtimeAuthHandler(async (token) => {
48
+ * // Validate the token
49
+ * console.log(token);
50
+ *
51
+ * // Return the topics to subscribe and publish
52
+ * return {
53
+ * subscribe: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
54
+ * publish: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
55
+ * };
56
+ * });
57
+ * ```
58
+ */
59
+ export declare function RealtimeAuthHandler(input: (token: string) => Promise<RealtimeAuthResult>): IoTCustomAuthorizerHandler;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Creates an authorization handler for the `Realtime` component, that validates
3
+ * the token and grants permissions for the topics the client can subscribe and publish to.
4
+ *
5
+ * @example
6
+ * ```js
7
+ * import { RealtimeAuthHandler, Resource } from "sst";
8
+ *
9
+ * export const handler = RealtimeAuthHandler(async (token) => {
10
+ * // Validate the token
11
+ * console.log(token);
12
+ *
13
+ * // Return the topics to subscribe and publish
14
+ * return {
15
+ * subscribe: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
16
+ * publish: [`${Resource.App.name}/${Resource.App.stage}/chat/room1`],
17
+ * };
18
+ * });
19
+ * ```
20
+ */
21
+ export function RealtimeAuthHandler(input) {
22
+ return async (evt, context) => {
23
+ const [, , , region, accountId] = context.invokedFunctionArn.split(":");
24
+ const token = Buffer.from(evt.protocolData.mqtt?.password ?? "", "base64").toString();
25
+ const ret = await input(token);
26
+ return {
27
+ isAuthenticated: true,
28
+ principalId: Date.now().toString(),
29
+ disconnectAfterInSeconds: 86400,
30
+ refreshAfterInSeconds: 300,
31
+ policyDocuments: [
32
+ {
33
+ Version: "2012-10-17",
34
+ Statement: [
35
+ {
36
+ Action: "iot:Connect",
37
+ Effect: "Allow",
38
+ Resource: "*",
39
+ },
40
+ ...(ret.subscribe
41
+ ? [
42
+ {
43
+ Action: "iot:Receive",
44
+ Effect: "Allow",
45
+ Resource: ret.subscribe.map((t) => `arn:aws:iot:${region}:${accountId}:topic/${t}`),
46
+ },
47
+ ]
48
+ : []),
49
+ ...(ret.subscribe
50
+ ? [
51
+ {
52
+ Action: "iot:Subscribe",
53
+ Effect: "Allow",
54
+ Resource: ret.subscribe.map((t) => `arn:aws:iot:${region}:${accountId}:topicfilter/${t}`),
55
+ },
56
+ ]
57
+ : []),
58
+ ...(ret.publish
59
+ ? [
60
+ {
61
+ Action: "iot:Publish",
62
+ Effect: "Allow",
63
+ Resource: ret.publish.map((t) => `arn:aws:iot:${region}:${accountId}:topic/${t}`),
64
+ },
65
+ ]
66
+ : []),
67
+ ],
68
+ },
69
+ ],
70
+ };
71
+ };
72
+ }
@@ -1,5 +1,8 @@
1
1
  export interface Resource {
2
- [key: string]: any;
2
+ App: {
3
+ name: string;
4
+ stage: string;
5
+ };
3
6
  }
4
7
  export declare function fromCloudflareEnv(input: any): void;
5
8
  export declare function wrapCloudflareHandler(handler: any): any;
package/dist/resource.js CHANGED
@@ -20,6 +20,14 @@ export function fromCloudflareEnv(input) {
20
20
  }
21
21
  }
22
22
  export function wrapCloudflareHandler(handler) {
23
+ if (typeof handler === "function" && handler.hasOwnProperty("prototype")) {
24
+ return class extends handler {
25
+ constructor(ctx, env) {
26
+ fromCloudflareEnv(env);
27
+ super(ctx, env);
28
+ }
29
+ };
30
+ }
23
31
  function wrap(fn) {
24
32
  return function (req, env, ...rest) {
25
33
  fromCloudflareEnv(env);
@@ -0,0 +1,217 @@
1
+ import { Resource } from "../resource.js";
2
+ export interface IngestEvent {
3
+ /**
4
+ * The text used to generate the embedding vector.
5
+ * At least one of `text` or `image` must be provided.
6
+ * @example
7
+ * ```js
8
+ * {
9
+ * text: "This is an example text.",
10
+ * }
11
+ * ```
12
+ */
13
+ text?: string;
14
+ /**
15
+ * The base64 representation of the image used to generate the embedding vector.
16
+ * At least one of `text` or `image` must be provided.
17
+ * @example
18
+ * ```js
19
+ * {
20
+ * image: await fs.readFile("./file.jpg").toString("base64"),
21
+ * }
22
+ * ```
23
+ */
24
+ image?: string;
25
+ /**
26
+ * Metadata for the event in JSON format.
27
+ * This metadata will be used to filter when retrieving and removing embeddings.
28
+ * @example
29
+ * ```js
30
+ * {
31
+ * metadata: {
32
+ * type: "movie",
33
+ * id: "movie-123",
34
+ * name: "Spiderman",
35
+ * }
36
+ * }
37
+ * ```
38
+ */
39
+ metadata: Record<string, any>;
40
+ }
41
+ export interface RetrieveEvent {
42
+ /**
43
+ * The text prompt used to retrieve embeddings.
44
+ * At least one of `text` or `image` must be provided.
45
+ * @example
46
+ * ```js
47
+ * {
48
+ * text: "This is an example text.",
49
+ * }
50
+ * ```
51
+ */
52
+ text?: string;
53
+ /**
54
+ * The base64 representation of the image prompt used to retrive embeddings.
55
+ * At least one of `text` or `image` must be provided.
56
+ * @example
57
+ * ```js
58
+ * {
59
+ * image: await fs.readFile("./file.jpg").toString("base64"),
60
+ * }
61
+ * ```
62
+ */
63
+ image?: string;
64
+ /**
65
+ * The metadata used to filter the retrieval of embeddings.
66
+ * Only embeddings with metadata that match the provided fields will be returned.
67
+ * @example
68
+ * ```js
69
+ * {
70
+ * include: {
71
+ * type: "movie",
72
+ * release: "2001",
73
+ * }
74
+ * }
75
+ * ```
76
+ * This will match the embedding with metadata:
77
+ * ```js
78
+ * {
79
+ * type: "movie",
80
+ * name: "Spiderman",
81
+ * release: "2001",
82
+ * }
83
+ * ```
84
+ *
85
+ * But not the embedding with metadata:
86
+ * ```js
87
+ * {
88
+ * type: "book",
89
+ * name: "Spiderman",
90
+ * release: "2001",
91
+ * }
92
+ * ```
93
+ */
94
+ include: Record<string, any>;
95
+ /**
96
+ * Exclude embeddings with metadata that match the provided fields.
97
+ * @example
98
+ * ```js
99
+ * {
100
+ * include: {
101
+ * type: "movie",
102
+ * release: "2001",
103
+ * },
104
+ * exclude: {
105
+ * name: "Spiderman",
106
+ * }
107
+ * }
108
+ * ```
109
+ * This will match the embedding with metadata:
110
+ * ```js
111
+ * {
112
+ * type: "movie",
113
+ * name: "A Beautiful Mind",
114
+ * release: "2001",
115
+ * }
116
+ * ```
117
+ *
118
+ * But not the embedding with metadata:
119
+ * ```js
120
+ * {
121
+ * type: "book",
122
+ * name: "Spiderman",
123
+ * release: "2001",
124
+ * }
125
+ * ```
126
+ */
127
+ exclude?: Record<string, any>;
128
+ /**
129
+ * The threshold of similarity between the prompt and the retrieved embeddings.
130
+ * Only embeddings with a similarity score higher than the threshold will be returned.
131
+ * Expected value is between 0 and 1.
132
+ * - 0 means the prompt and the retrieved embeddings are completely different.
133
+ * - 1 means the prompt and the retrieved embeddings are identical.
134
+ * @default `0`
135
+ * @example
136
+ * ```js
137
+ * {
138
+ * threshold: 0.5,
139
+ * }
140
+ * ```
141
+ */
142
+ threshold?: number;
143
+ /**
144
+ * The number of results to return.
145
+ * @default `10`
146
+ * @example
147
+ * ```js
148
+ * {
149
+ * count: 10,
150
+ * }
151
+ * ```
152
+ */
153
+ count?: number;
154
+ }
155
+ export interface RemoveEvent {
156
+ /**
157
+ * The metadata used to filter the removal of embeddings.
158
+ * Only embeddings with metadata that match the provided fields will be removed.
159
+ * @example
160
+ * To remove embeddings for movie with id "movie-123":
161
+ * ```js
162
+ * {
163
+ * include: {
164
+ * id: "movie-123",
165
+ * }
166
+ * }
167
+ * ```
168
+ * To remove embeddings for all movies:
169
+ * ```js
170
+ * {
171
+ * include: {
172
+ * type: "movie",
173
+ * }
174
+ * }
175
+ * ```
176
+ */
177
+ include: Record<string, any>;
178
+ }
179
+ export interface RetrieveResponse {
180
+ /**
181
+ * Metadata for the event in JSON format that was provided when ingesting the embedding.
182
+ */
183
+ metadata: Record<string, any>;
184
+ /**
185
+ * The similarity score between the prompt and the retrieved embedding.
186
+ */
187
+ score: number;
188
+ }
189
+ export interface VectorClientResponse {
190
+ ingest: (event: IngestEvent) => Promise<void>;
191
+ retrieve: (event: RetrieveEvent) => Promise<RetrieveResponse>;
192
+ remove: (event: RemoveEvent) => Promise<void>;
193
+ }
194
+ /**
195
+ * Create a client to interact with the Vector database.
196
+ * @example
197
+ * ```js
198
+ * import { VectorClient } from "sst";
199
+ * const client = VectorClient("MyVectorDB");
200
+ *
201
+ * // Ingest a text into the vector db
202
+ * await client.ingest({
203
+ * text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
204
+ * metadata: { type: "movie", genre: "comedy" },
205
+ * });
206
+ *
207
+ * // Retrieve embeddings similar to the provided text
208
+ * const result = await client.retrieve({
209
+ * text: "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
210
+ * include: { type: "movie" },
211
+ * exclude: { genre: "thriller" },
212
+ * });
213
+ * ```
214
+ */
215
+ export declare function VectorClient<T extends keyof {
216
+ [key in keyof Resource as "sst.aws.Vector" extends Resource[key]["type"] ? string extends key ? never : key : never]: Resource[key];
217
+ }>(name: T): VectorClientResponse;
@@ -0,0 +1,62 @@
1
+ import { LambdaClient, InvokeCommand, } from "@aws-sdk/client-lambda";
2
+ import { Resource } from "../resource.js";
3
+ const lambda = new LambdaClient();
4
+ /**
5
+ * Create a client to interact with the Vector database.
6
+ * @example
7
+ * ```js
8
+ * import { VectorClient } from "sst";
9
+ * const client = VectorClient("MyVectorDB");
10
+ *
11
+ * // Ingest a text into the vector db
12
+ * await client.ingest({
13
+ * text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
14
+ * metadata: { type: "movie", genre: "comedy" },
15
+ * });
16
+ *
17
+ * // Retrieve embeddings similar to the provided text
18
+ * const result = await client.retrieve({
19
+ * text: "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
20
+ * include: { type: "movie" },
21
+ * exclude: { genre: "thriller" },
22
+ * });
23
+ * ```
24
+ */
25
+ export function VectorClient(name) {
26
+ return {
27
+ ingest: async (event) => {
28
+ const ret = await lambda.send(new InvokeCommand({
29
+ // @ts-expect-error
30
+ FunctionName: Resource[name].ingestor,
31
+ Payload: JSON.stringify(event),
32
+ }));
33
+ parsePayload(ret, "Failed to ingest into the vector db");
34
+ },
35
+ retrieve: async (event) => {
36
+ const ret = await lambda.send(new InvokeCommand({
37
+ // @ts-expect-error
38
+ FunctionName: Resource[name].retriever,
39
+ Payload: JSON.stringify(event),
40
+ }));
41
+ return parsePayload(ret, "Failed to retrieve from the vector db");
42
+ },
43
+ remove: async (event) => {
44
+ const ret = await lambda.send(new InvokeCommand({
45
+ // @ts-expect-error
46
+ FunctionName: Resource[name].remover,
47
+ Payload: JSON.stringify(event),
48
+ }));
49
+ parsePayload(ret, "Failed to remove from the vector db");
50
+ },
51
+ };
52
+ }
53
+ function parsePayload(output, message) {
54
+ const payload = JSON.parse(Buffer.from(output.Payload).toString());
55
+ // Set cause to the payload so that it can be logged in CloudWatch
56
+ if (output.FunctionError) {
57
+ const e = new Error(message);
58
+ e.cause = payload;
59
+ throw e;
60
+ }
61
+ return payload;
62
+ }
@@ -5,6 +5,7 @@ export function VectorClient(name) {
5
5
  return {
6
6
  ingest: async (event) => {
7
7
  const ret = await lambda.send(new InvokeCommand({
8
+ // @ts-expect-error
8
9
  FunctionName: Resource[name].ingestor,
9
10
  Payload: JSON.stringify(event),
10
11
  }));
@@ -12,6 +13,7 @@ export function VectorClient(name) {
12
13
  },
13
14
  retrieve: async (event) => {
14
15
  const ret = await lambda.send(new InvokeCommand({
16
+ // @ts-expect-error
15
17
  FunctionName: Resource[name].retriever,
16
18
  Payload: JSON.stringify(event),
17
19
  }));
@@ -19,6 +21,7 @@ export function VectorClient(name) {
19
21
  },
20
22
  remove: async (event) => {
21
23
  const ret = await lambda.send(new InvokeCommand({
24
+ // @ts-expect-error
22
25
  FunctionName: Resource[name].remover,
23
26
  Payload: JSON.stringify(event),
24
27
  }));
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "sst",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
- "version": "3.0.13",
6
+ "version": "3.0.15",
7
7
  "main": "./dist/index.js",
8
8
  "exports": {
9
9
  ".": "./dist/index.js",