@salesforce/mrt-utilities 0.1.1 → 0.1.3
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/dist/cjs/data-store/index.d.ts +56 -0
- package/dist/cjs/data-store/index.js +131 -0
- package/dist/cjs/data-store/index.js.map +1 -0
- package/dist/cjs/index.js +22 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metrics/index.js +17 -1
- package/dist/cjs/metrics/index.js.map +1 -1
- package/dist/cjs/metrics/metrics-sender.js +14 -9
- package/dist/cjs/metrics/metrics-sender.js.map +1 -1
- package/dist/cjs/middleware/data-store.d.ts +3 -54
- package/dist/cjs/middleware/data-store.js +19 -116
- package/dist/cjs/middleware/data-store.js.map +1 -1
- package/dist/cjs/middleware/express.d.ts +2 -0
- package/dist/cjs/middleware/express.js +23 -0
- package/dist/cjs/middleware/express.js.map +1 -0
- package/dist/cjs/middleware/index.d.ts +2 -3
- package/dist/cjs/middleware/index.js +18 -2
- package/dist/cjs/middleware/index.js.map +1 -1
- package/dist/cjs/middleware/middleware.js +79 -34
- package/dist/cjs/middleware/middleware.js.map +1 -1
- package/dist/cjs/streaming/create-lambda-adapter.js +66 -76
- package/dist/cjs/streaming/create-lambda-adapter.js.map +1 -1
- package/dist/cjs/streaming/index.js +7 -1
- package/dist/cjs/streaming/index.js.map +1 -1
- package/dist/cjs/utils/configure-proxying.js +16 -10
- package/dist/cjs/utils/configure-proxying.js.map +1 -1
- package/dist/cjs/utils/ssr-proxying.js +43 -32
- package/dist/cjs/utils/ssr-proxying.js.map +1 -1
- package/dist/cjs/utils/utils.js +7 -2
- package/dist/cjs/utils/utils.js.map +1 -1
- package/dist/esm/data-store/index.d.ts +56 -0
- package/dist/esm/data-store/index.js +124 -0
- package/dist/esm/data-store/index.js.map +1 -0
- package/dist/esm/middleware/data-store.d.ts +3 -54
- package/dist/esm/middleware/data-store.js +3 -116
- package/dist/esm/middleware/data-store.js.map +1 -1
- package/dist/esm/middleware/express.d.ts +2 -0
- package/dist/esm/middleware/express.js +8 -0
- package/dist/esm/middleware/express.js.map +1 -0
- package/dist/esm/middleware/index.d.ts +2 -3
- package/dist/esm/middleware/index.js +2 -3
- package/dist/esm/middleware/index.js.map +1 -1
- package/dist/esm/streaming/create-lambda-adapter.js +39 -57
- package/dist/esm/streaming/create-lambda-adapter.js.map +1 -1
- package/package.json +24 -2
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
export declare class DataStoreNotFoundError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class DataStoreServiceError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class DataStoreUnavailableError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A class for reading entries from the data store.
|
|
13
|
+
*
|
|
14
|
+
* This class uses a singleton pattern.
|
|
15
|
+
* Use DataStore.getDataStore() to get the singleton instance.
|
|
16
|
+
*/
|
|
17
|
+
export declare class DataStore {
|
|
18
|
+
private _tableName;
|
|
19
|
+
private _ddb;
|
|
20
|
+
private static _instance;
|
|
21
|
+
/** @internal Test hook: inject a document client for unit tests */
|
|
22
|
+
static _testDocumentClient: DynamoDBDocumentClient | null;
|
|
23
|
+
/** @internal Test hook: inject logMRTError for unit tests */
|
|
24
|
+
static _testLogMRTError: ((namespace: string, err: unknown, context?: Record<string, unknown>) => void) | null;
|
|
25
|
+
private constructor();
|
|
26
|
+
/**
|
|
27
|
+
* Get or create a DynamoDB document client (for abstraction of attribute values).
|
|
28
|
+
*
|
|
29
|
+
* @private
|
|
30
|
+
* @returns The DynamoDB document client
|
|
31
|
+
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
32
|
+
*/
|
|
33
|
+
private getClient;
|
|
34
|
+
/**
|
|
35
|
+
* Get or create the singleton DataStore instance.
|
|
36
|
+
*
|
|
37
|
+
* @returns The singleton DataStore instance
|
|
38
|
+
*/
|
|
39
|
+
static getDataStore(): DataStore;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the data store can be used in the current environment.
|
|
42
|
+
*
|
|
43
|
+
* @returns true if the data store is available, false otherwise
|
|
44
|
+
*/
|
|
45
|
+
isDataStoreAvailable(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Fetch an entry from the data store.
|
|
48
|
+
*
|
|
49
|
+
* @param key The data store entry's key
|
|
50
|
+
* @returns An object containing the entry's key and value
|
|
51
|
+
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
52
|
+
* @throws {DataStoreNotFoundError} An entry with the given key cannot be found
|
|
53
|
+
* @throws {DataStoreServiceError} An internal error occurred
|
|
54
|
+
*/
|
|
55
|
+
getEntry(key: string): Promise<Record<string, unknown> | undefined>;
|
|
56
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
7
|
+
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb';
|
|
8
|
+
import { logMRTError } from '../utils/utils.js';
|
|
9
|
+
export class DataStoreNotFoundError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'DataStoreNotFoundError';
|
|
13
|
+
Object.setPrototypeOf(this, DataStoreNotFoundError.prototype);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class DataStoreServiceError extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'DataStoreServiceError';
|
|
20
|
+
Object.setPrototypeOf(this, DataStoreServiceError.prototype);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class DataStoreUnavailableError extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'DataStoreUnavailableError';
|
|
27
|
+
Object.setPrototypeOf(this, DataStoreUnavailableError.prototype);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A class for reading entries from the data store.
|
|
32
|
+
*
|
|
33
|
+
* This class uses a singleton pattern.
|
|
34
|
+
* Use DataStore.getDataStore() to get the singleton instance.
|
|
35
|
+
*/
|
|
36
|
+
export class DataStore {
|
|
37
|
+
_tableName = '';
|
|
38
|
+
_ddb = null;
|
|
39
|
+
static _instance = null;
|
|
40
|
+
/** @internal Test hook: inject a document client for unit tests */
|
|
41
|
+
static _testDocumentClient = null;
|
|
42
|
+
/** @internal Test hook: inject logMRTError for unit tests */
|
|
43
|
+
static _testLogMRTError = null;
|
|
44
|
+
constructor() {
|
|
45
|
+
// Private constructor for singleton; use DataStore.getDataStore() instead.
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get or create a DynamoDB document client (for abstraction of attribute values).
|
|
49
|
+
*
|
|
50
|
+
* @private
|
|
51
|
+
* @returns The DynamoDB document client
|
|
52
|
+
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
53
|
+
*/
|
|
54
|
+
getClient() {
|
|
55
|
+
if (!this.isDataStoreAvailable()) {
|
|
56
|
+
throw new DataStoreUnavailableError('The data store is unavailable.');
|
|
57
|
+
}
|
|
58
|
+
if (DataStore._testDocumentClient) {
|
|
59
|
+
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
|
|
60
|
+
return DataStore._testDocumentClient;
|
|
61
|
+
}
|
|
62
|
+
if (!this._ddb) {
|
|
63
|
+
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
|
|
64
|
+
this._ddb = DynamoDBDocumentClient.from(new DynamoDBClient({
|
|
65
|
+
region: process.env.AWS_REGION,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
return this._ddb;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get or create the singleton DataStore instance.
|
|
72
|
+
*
|
|
73
|
+
* @returns The singleton DataStore instance
|
|
74
|
+
*/
|
|
75
|
+
static getDataStore() {
|
|
76
|
+
if (!DataStore._instance) {
|
|
77
|
+
DataStore._instance = new DataStore();
|
|
78
|
+
}
|
|
79
|
+
return DataStore._instance;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Whether the data store can be used in the current environment.
|
|
83
|
+
*
|
|
84
|
+
* @returns true if the data store is available, false otherwise
|
|
85
|
+
*/
|
|
86
|
+
isDataStoreAvailable() {
|
|
87
|
+
return Boolean(process.env.AWS_REGION && process.env.MOBIFY_PROPERTY_ID && process.env.DEPLOY_TARGET);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Fetch an entry from the data store.
|
|
91
|
+
*
|
|
92
|
+
* @param key The data store entry's key
|
|
93
|
+
* @returns An object containing the entry's key and value
|
|
94
|
+
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
95
|
+
* @throws {DataStoreNotFoundError} An entry with the given key cannot be found
|
|
96
|
+
* @throws {DataStoreServiceError} An internal error occurred
|
|
97
|
+
*/
|
|
98
|
+
async getEntry(key) {
|
|
99
|
+
if (!this.isDataStoreAvailable()) {
|
|
100
|
+
throw new DataStoreUnavailableError('The data store is unavailable.');
|
|
101
|
+
}
|
|
102
|
+
const ddb = this.getClient();
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await ddb.send(new GetCommand({
|
|
106
|
+
TableName: this._tableName,
|
|
107
|
+
Key: {
|
|
108
|
+
projectEnvironment: `${process.env.MOBIFY_PROPERTY_ID} ${process.env.DEPLOY_TARGET}`,
|
|
109
|
+
key,
|
|
110
|
+
},
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const logFn = DataStore._testLogMRTError ?? logMRTError;
|
|
115
|
+
logFn('data_store', error, { key, tableName: this._tableName });
|
|
116
|
+
throw new DataStoreServiceError('Data store request failed.');
|
|
117
|
+
}
|
|
118
|
+
if (!response.Item?.value) {
|
|
119
|
+
throw new DataStoreNotFoundError(`Data store entry '${key}' not found.`);
|
|
120
|
+
}
|
|
121
|
+
return { key, value: response.Item.value };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/data-store/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,56 +1,5 @@
|
|
|
1
|
-
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
2
|
-
export declare class DataStoreNotFoundError extends Error {
|
|
3
|
-
constructor(message: string);
|
|
4
|
-
}
|
|
5
|
-
export declare class DataStoreServiceError extends Error {
|
|
6
|
-
constructor(message: string);
|
|
7
|
-
}
|
|
8
|
-
export declare class DataStoreUnavailableError extends Error {
|
|
9
|
-
constructor(message: string);
|
|
10
|
-
}
|
|
11
1
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* This class uses a singleton pattern.
|
|
15
|
-
* Use DataStore.getDataStore() to get the singleton instance.
|
|
2
|
+
* @deprecated Import data-store symbols from `@salesforce/mrt-utilities/data-store`.
|
|
3
|
+
* This compatibility path is kept for backward compatibility.
|
|
16
4
|
*/
|
|
17
|
-
export
|
|
18
|
-
private _tableName;
|
|
19
|
-
private _ddb;
|
|
20
|
-
private static _instance;
|
|
21
|
-
/** @internal Test hook: inject a document client for unit tests */
|
|
22
|
-
static _testDocumentClient: DynamoDBDocumentClient | null;
|
|
23
|
-
/** @internal Test hook: inject logMRTError for unit tests */
|
|
24
|
-
static _testLogMRTError: ((namespace: string, err: unknown, context?: Record<string, unknown>) => void) | null;
|
|
25
|
-
private constructor();
|
|
26
|
-
/**
|
|
27
|
-
* Get or create a DynamoDB document client (for abstraction of attribute values).
|
|
28
|
-
*
|
|
29
|
-
* @private
|
|
30
|
-
* @returns The DynamoDB document client
|
|
31
|
-
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
32
|
-
*/
|
|
33
|
-
private getClient;
|
|
34
|
-
/**
|
|
35
|
-
* Get or create the singleton DataStore instance.
|
|
36
|
-
*
|
|
37
|
-
* @returns The singleton DataStore instance
|
|
38
|
-
*/
|
|
39
|
-
static getDataStore(): DataStore;
|
|
40
|
-
/**
|
|
41
|
-
* Whether the data store can be used in the current environment.
|
|
42
|
-
*
|
|
43
|
-
* @returns true if the data store is available, false otherwise
|
|
44
|
-
*/
|
|
45
|
-
isDataStoreAvailable(): boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Fetch an entry from the data store.
|
|
48
|
-
*
|
|
49
|
-
* @param key The data store entry's key
|
|
50
|
-
* @returns An object containing the entry's key and value
|
|
51
|
-
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
52
|
-
* @throws {DataStoreNotFoundError} An entry with the given key cannot be found
|
|
53
|
-
* @throws {DataStoreServiceError} An internal error occurred
|
|
54
|
-
*/
|
|
55
|
-
getEntry(key: string): Promise<Record<string, unknown> | undefined>;
|
|
56
|
-
}
|
|
5
|
+
export * from '../data-store/index.js';
|
|
@@ -3,122 +3,9 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2
|
|
4
4
|
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
7
|
-
import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb';
|
|
8
|
-
import { logMRTError } from '../utils/utils.js';
|
|
9
|
-
export class DataStoreNotFoundError extends Error {
|
|
10
|
-
constructor(message) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = 'DataStoreNotFoundError';
|
|
13
|
-
Object.setPrototypeOf(this, DataStoreNotFoundError.prototype);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export class DataStoreServiceError extends Error {
|
|
17
|
-
constructor(message) {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = 'DataStoreServiceError';
|
|
20
|
-
Object.setPrototypeOf(this, DataStoreServiceError.prototype);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
export class DataStoreUnavailableError extends Error {
|
|
24
|
-
constructor(message) {
|
|
25
|
-
super(message);
|
|
26
|
-
this.name = 'DataStoreUnavailableError';
|
|
27
|
-
Object.setPrototypeOf(this, DataStoreUnavailableError.prototype);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
6
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* This class uses a singleton pattern.
|
|
34
|
-
* Use DataStore.getDataStore() to get the singleton instance.
|
|
7
|
+
* @deprecated Import data-store symbols from `@salesforce/mrt-utilities/data-store`.
|
|
8
|
+
* This compatibility path is kept for backward compatibility.
|
|
35
9
|
*/
|
|
36
|
-
export
|
|
37
|
-
_tableName = '';
|
|
38
|
-
_ddb = null;
|
|
39
|
-
static _instance = null;
|
|
40
|
-
/** @internal Test hook: inject a document client for unit tests */
|
|
41
|
-
static _testDocumentClient = null;
|
|
42
|
-
/** @internal Test hook: inject logMRTError for unit tests */
|
|
43
|
-
static _testLogMRTError = null;
|
|
44
|
-
constructor() {
|
|
45
|
-
// Private constructor for singleton; use DataStore.getDataStore() instead.
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get or create a DynamoDB document client (for abstraction of attribute values).
|
|
49
|
-
*
|
|
50
|
-
* @private
|
|
51
|
-
* @returns The DynamoDB document client
|
|
52
|
-
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
53
|
-
*/
|
|
54
|
-
getClient() {
|
|
55
|
-
if (!this.isDataStoreAvailable()) {
|
|
56
|
-
throw new DataStoreUnavailableError('The data store is unavailable.');
|
|
57
|
-
}
|
|
58
|
-
if (DataStore._testDocumentClient) {
|
|
59
|
-
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
|
|
60
|
-
return DataStore._testDocumentClient;
|
|
61
|
-
}
|
|
62
|
-
if (!this._ddb) {
|
|
63
|
-
this._tableName = `DataAccessLayer-${process.env.AWS_REGION}`;
|
|
64
|
-
this._ddb = DynamoDBDocumentClient.from(new DynamoDBClient({
|
|
65
|
-
region: process.env.AWS_REGION,
|
|
66
|
-
}));
|
|
67
|
-
}
|
|
68
|
-
return this._ddb;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get or create the singleton DataStore instance.
|
|
72
|
-
*
|
|
73
|
-
* @returns The singleton DataStore instance
|
|
74
|
-
*/
|
|
75
|
-
static getDataStore() {
|
|
76
|
-
if (!DataStore._instance) {
|
|
77
|
-
DataStore._instance = new DataStore();
|
|
78
|
-
}
|
|
79
|
-
return DataStore._instance;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Whether the data store can be used in the current environment.
|
|
83
|
-
*
|
|
84
|
-
* @returns true if the data store is available, false otherwise
|
|
85
|
-
*/
|
|
86
|
-
isDataStoreAvailable() {
|
|
87
|
-
return Boolean(process.env.AWS_REGION && process.env.MOBIFY_PROPERTY_ID && process.env.DEPLOY_TARGET);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Fetch an entry from the data store.
|
|
91
|
-
*
|
|
92
|
-
* @param key The data store entry's key
|
|
93
|
-
* @returns An object containing the entry's key and value
|
|
94
|
-
* @throws {DataStoreUnavailableError} The data store is unavailable
|
|
95
|
-
* @throws {DataStoreNotFoundError} An entry with the given key cannot be found
|
|
96
|
-
* @throws {DataStoreServiceError} An internal error occurred
|
|
97
|
-
*/
|
|
98
|
-
async getEntry(key) {
|
|
99
|
-
if (!this.isDataStoreAvailable()) {
|
|
100
|
-
throw new DataStoreUnavailableError('The data store is unavailable.');
|
|
101
|
-
}
|
|
102
|
-
const ddb = this.getClient();
|
|
103
|
-
let response;
|
|
104
|
-
try {
|
|
105
|
-
response = await ddb.send(new GetCommand({
|
|
106
|
-
TableName: this._tableName,
|
|
107
|
-
Key: {
|
|
108
|
-
projectEnvironment: `${process.env.MOBIFY_PROPERTY_ID} ${process.env.DEPLOY_TARGET}`,
|
|
109
|
-
key,
|
|
110
|
-
},
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
const logFn = DataStore._testLogMRTError ?? logMRTError;
|
|
115
|
-
logFn('data_store', error, { key, tableName: this._tableName });
|
|
116
|
-
throw new DataStoreServiceError('Data store request failed.');
|
|
117
|
-
}
|
|
118
|
-
if (!response.Item?.value) {
|
|
119
|
-
throw new DataStoreNotFoundError(`Data store entry '${key}' not found.`);
|
|
120
|
-
}
|
|
121
|
-
return { key, value: response.Item.value };
|
|
122
|
-
}
|
|
123
|
-
}
|
|
10
|
+
export * from '../data-store/index.js';
|
|
124
11
|
//# sourceMappingURL=data-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-store.js","sourceRoot":"","sources":["../../../src/middleware/data-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH
|
|
1
|
+
{"version":3,"file":"data-store.js","sourceRoot":"","sources":["../../../src/middleware/data-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
export * from './middleware.js';
|
|
7
|
+
export {} from '../utils/configure-proxying.js';
|
|
8
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAkB,MAAM,gCAAgC,CAAC"}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export * from '
|
|
2
|
-
export * from './
|
|
3
|
-
export { type ProxyConfig } from '../utils/configure-proxying.js';
|
|
1
|
+
export * from '../data-store/index.js';
|
|
2
|
+
export * from './express.js';
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2
|
|
4
4
|
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
5
|
*/
|
|
6
|
-
export * from '
|
|
7
|
-
export * from './
|
|
8
|
-
export {} from '../utils/configure-proxying.js';
|
|
6
|
+
export * from '../data-store/index.js';
|
|
7
|
+
export * from './express.js';
|
|
9
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC"}
|
|
@@ -44,7 +44,7 @@ export function createStreamingLambdaAdapter(app, responseStream, compressionCon
|
|
|
44
44
|
const isStreamOpen = responseStream && responseStream.writable && !responseStream.destroyed && !responseStream.writableEnded;
|
|
45
45
|
if (isStreamOpen && typeof responseStream.write === 'function') {
|
|
46
46
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
47
|
-
responseStream.write(`
|
|
47
|
+
responseStream.write(`Internal Server Error: ${errorMessage}`);
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
50
|
console.error('[error handler] Cannot write error - stream is closed');
|
|
@@ -81,34 +81,17 @@ async function streamResponse(event, responseStream, context, app, compressionCo
|
|
|
81
81
|
reject(err);
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
+
// See events defined for hhtp.serverResponse interface here:
|
|
85
|
+
// https://nodejs.org/docs/latest-v24.x/api/http.html#class-httpserverresponse
|
|
84
86
|
// Handle response finish
|
|
85
|
-
expressResponse.once('finish',
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
expressResponse.once('finish', resolveOnce);
|
|
88
|
+
// handle response close, either after a 'finish' or if underlying
|
|
89
|
+
// connection is closed early
|
|
90
|
+
expressResponse.once('close', resolveOnce);
|
|
88
91
|
// Handle response errors
|
|
89
|
-
expressResponse.once('error',
|
|
90
|
-
rejectOnce(err);
|
|
91
|
-
});
|
|
92
|
+
expressResponse.once('error', rejectOnce);
|
|
92
93
|
try {
|
|
93
|
-
app(expressRequest, expressResponse
|
|
94
|
-
if (err) {
|
|
95
|
-
console.error('Express app error:', err);
|
|
96
|
-
rejectOnce(err);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// If response has finished, resolveOnce will be called by the finish event
|
|
100
|
-
// Otherwise, resolve after a short delay to allow async operations
|
|
101
|
-
if (expressResponse.finished) {
|
|
102
|
-
resolveOnce();
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
// Wait a bit for the response to finish
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
resolveOnce();
|
|
108
|
-
}, 10);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
});
|
|
94
|
+
app(expressRequest, expressResponse);
|
|
112
95
|
}
|
|
113
96
|
catch (error) {
|
|
114
97
|
console.error('Error in streamResponse:', error);
|
|
@@ -410,6 +393,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
410
393
|
}
|
|
411
394
|
if (!isStreamOpen()) {
|
|
412
395
|
console.error('Cannot initialize response - stream is closed');
|
|
396
|
+
emitCloseOnce(response);
|
|
413
397
|
return;
|
|
414
398
|
}
|
|
415
399
|
// Collect all current headers from the response
|
|
@@ -432,7 +416,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
432
416
|
// Create HttpResponseStream with metadata
|
|
433
417
|
// This writes the HTTP status and headers to the stream
|
|
434
418
|
const metadata = {
|
|
435
|
-
statusCode,
|
|
419
|
+
statusCode: response.statusCode || statusCode,
|
|
436
420
|
headers,
|
|
437
421
|
};
|
|
438
422
|
const cookies = metadata.headers['set-cookie'];
|
|
@@ -442,6 +426,14 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
442
426
|
}
|
|
443
427
|
metadata.headers = convertHeaders(metadata.headers);
|
|
444
428
|
httpResponseStream = awslambda.HttpResponseStream.from(responseStream, metadata);
|
|
429
|
+
// Bind 'finish' and 'close' handlers to emit the same events on the response object
|
|
430
|
+
httpResponseStream.on('finish', () => {
|
|
431
|
+
response.finished = true;
|
|
432
|
+
response.emit('finish');
|
|
433
|
+
});
|
|
434
|
+
httpResponseStream.on('close', () => {
|
|
435
|
+
response.emit('close');
|
|
436
|
+
});
|
|
445
437
|
// Set up compression stream if compression is enabled
|
|
446
438
|
// The compression stream pipes to httpResponseStream, which pipes to responseStream
|
|
447
439
|
// 'identity' means no encoding, so we should not initialize compression for it
|
|
@@ -529,7 +521,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
529
521
|
headerObj = reasonPhrase;
|
|
530
522
|
reasonPhrase = undefined;
|
|
531
523
|
}
|
|
532
|
-
statusCode = code || statusCode;
|
|
524
|
+
statusCode = code || this.statusCode || statusCode;
|
|
533
525
|
this.statusCode = statusCode;
|
|
534
526
|
// Set statusMessage if provided
|
|
535
527
|
if (reasonPhrase) {
|
|
@@ -552,6 +544,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
552
544
|
res.write = function (chunk) {
|
|
553
545
|
if (!isStreamOpen()) {
|
|
554
546
|
console.error(`Cannot write - stream is closed`);
|
|
547
|
+
emitCloseOnce(this);
|
|
555
548
|
return false;
|
|
556
549
|
}
|
|
557
550
|
initializeResponse(this);
|
|
@@ -575,6 +568,14 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
575
568
|
httpResponseStream.flush();
|
|
576
569
|
}
|
|
577
570
|
};
|
|
571
|
+
let emittedClose = false;
|
|
572
|
+
const emitCloseOnce = (response) => {
|
|
573
|
+
if (emittedClose) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
response.emit('close');
|
|
577
|
+
emittedClose = true;
|
|
578
|
+
};
|
|
578
579
|
/**
|
|
579
580
|
* Ends the appropriate stream(s) and emits the finish event
|
|
580
581
|
* If compression is enabled, ends the compression stream which will automatically
|
|
@@ -583,50 +584,30 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
583
584
|
* @param response - The Express response object to emit finish event on
|
|
584
585
|
*/
|
|
585
586
|
const endStream = (response) => {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
// Flush compression stream to ensure all buffered data is written
|
|
589
|
-
_flush();
|
|
590
|
-
// End compression stream - this will automatically end httpResponseStream
|
|
591
|
-
// due to the pipe with { end: true } option
|
|
592
|
-
compressionStream.end(() => {
|
|
593
|
-
response.finished = true;
|
|
594
|
-
response.emit('finish');
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
catch (error) {
|
|
598
|
-
console.error(`Error ending compression stream:`, error);
|
|
599
|
-
// Still emit finish even if there was an error
|
|
600
|
-
response.finished = true;
|
|
601
|
-
response.emit('finish');
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
else if (httpResponseStream && httpResponseStream.writable) {
|
|
605
|
-
// No compression, end httpResponseStream directly
|
|
587
|
+
const stream = (shouldCompress && compressionStream) || (httpResponseStream?.writable && httpResponseStream);
|
|
588
|
+
if (stream) {
|
|
606
589
|
try {
|
|
607
590
|
_flush();
|
|
608
|
-
|
|
609
|
-
response.finished = true;
|
|
610
|
-
response.emit('finish');
|
|
611
|
-
});
|
|
591
|
+
stream.end();
|
|
612
592
|
}
|
|
613
593
|
catch (error) {
|
|
614
|
-
console.error(`Error ending
|
|
594
|
+
console.error(`Error ending stream:`, error);
|
|
615
595
|
response.finished = true;
|
|
616
|
-
response
|
|
596
|
+
emitCloseOnce(response);
|
|
617
597
|
}
|
|
618
598
|
}
|
|
619
599
|
else {
|
|
620
600
|
console.error(`Cannot call end() - stream is closed`);
|
|
621
601
|
// Still emit finish to prevent hanging
|
|
622
602
|
response.finished = true;
|
|
623
|
-
response
|
|
603
|
+
emitCloseOnce(response);
|
|
624
604
|
}
|
|
625
605
|
};
|
|
626
606
|
// @ts-expect-error - Type signature doesn't match ServerResponse.end exactly, but our implementation is compatible
|
|
627
607
|
res.end = function (chunk) {
|
|
628
608
|
if (!isStreamOpen()) {
|
|
629
609
|
console.error(`Cannot end - stream is already closed`);
|
|
610
|
+
emitCloseOnce(this);
|
|
630
611
|
return this;
|
|
631
612
|
}
|
|
632
613
|
initializeResponse(this);
|
|
@@ -654,9 +635,8 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
654
635
|
// Add Express-specific methods that aren't in ServerResponse
|
|
655
636
|
res.status = function (code, message) {
|
|
656
637
|
this.statusCode = code;
|
|
657
|
-
statusCode = code;
|
|
658
638
|
if (message !== undefined) {
|
|
659
|
-
statusMessage = message;
|
|
639
|
+
this.statusMessage = message;
|
|
660
640
|
}
|
|
661
641
|
return this;
|
|
662
642
|
};
|
|
@@ -700,6 +680,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
700
680
|
if (!responseStarted) {
|
|
701
681
|
if (!isStreamOpen()) {
|
|
702
682
|
console.error('[res.flushHeaders] Cannot flush headers - stream is closed');
|
|
683
|
+
emitCloseOnce(this);
|
|
703
684
|
return this;
|
|
704
685
|
}
|
|
705
686
|
// Collect all current headers and send them
|
|
@@ -739,6 +720,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
739
720
|
res.flush = function () {
|
|
740
721
|
if (!isStreamOpen()) {
|
|
741
722
|
console.error(`Cannot flush - stream is closed`);
|
|
723
|
+
emitCloseOnce(this);
|
|
742
724
|
return this;
|
|
743
725
|
}
|
|
744
726
|
initializeResponse(this);
|