@beesolve/aws-accounts 1.0.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/dist/error.js ADDED
@@ -0,0 +1,66 @@
1
+ class CliError extends Error {
2
+ kind;
3
+ constructor(kind, message) {
4
+ super(message);
5
+ this.kind = kind;
6
+ this.name = "CliError";
7
+ }
8
+ }
9
+ function toUsageError(message) {
10
+ return new CliError("usage", message);
11
+ }
12
+ function toValidationError(message) {
13
+ return new CliError("validation", message);
14
+ }
15
+ function toPreconditionError(message) {
16
+ return new CliError("precondition", message);
17
+ }
18
+ function classifyCliError(error) {
19
+ if (error instanceof CliError) {
20
+ return { kind: error.kind, message: error.message };
21
+ }
22
+ const message = error instanceof Error ? error.message : String(error);
23
+ if (isUsageErrorMessage(message)) {
24
+ return { kind: "usage", message };
25
+ }
26
+ if (isValidationErrorMessage(message)) {
27
+ return { kind: "validation", message };
28
+ }
29
+ if (isPreconditionErrorMessage(message)) {
30
+ return { kind: "precondition", message };
31
+ }
32
+ return { kind: "runtime", message };
33
+ }
34
+ function exitCodeForCliErrorKind(kind) {
35
+ if (kind === "usage") {
36
+ return 2;
37
+ }
38
+ if (kind === "validation") {
39
+ return 3;
40
+ }
41
+ if (kind === "precondition") {
42
+ return 4;
43
+ }
44
+ return 1;
45
+ }
46
+ function isUsageErrorMessage(message) {
47
+ return message.includes("Missing required --") || message.includes(
48
+ "Refusing to create organizational units in non-interactive mode without --yes."
49
+ ) || message.includes(
50
+ "Refusing to overwrite config files in non-interactive mode without --yes."
51
+ );
52
+ }
53
+ function isValidationErrorMessage(message) {
54
+ return message.includes("Invalid --");
55
+ }
56
+ function isPreconditionErrorMessage(message) {
57
+ return message.includes("Could not find") || message.includes("must exist") || message.includes("Re-run bootstrap") || message.includes("state/context mismatch") || message.includes("aws.context.json conflicts");
58
+ }
59
+ export {
60
+ CliError,
61
+ classifyCliError,
62
+ exitCodeForCliErrorKind,
63
+ toPreconditionError,
64
+ toUsageError,
65
+ toValidationError
66
+ };
@@ -0,0 +1,21 @@
1
+ function assertUnreachable(value, message = JSON.stringify(value)) {
2
+ throw Error("An unreachable state reached!\n" + message);
3
+ }
4
+ function toRecordByProperty(input, key, keyTransformer = (key2) => key2) {
5
+ return Object.fromEntries(
6
+ input.map((item) => [
7
+ keyTransformer(typeof key === "function" ? key(item) : item[key]),
8
+ item
9
+ ])
10
+ );
11
+ }
12
+ async function delay(ms) {
13
+ await new Promise((resolve) => {
14
+ setTimeout(resolve, ms);
15
+ });
16
+ }
17
+ export {
18
+ assertUnreachable,
19
+ delay,
20
+ toRecordByProperty
21
+ };
@@ -0,0 +1,375 @@
1
+ import {
2
+ GetObjectCommand,
3
+ PutObjectCommand,
4
+ S3Client,
5
+ S3ServiceException
6
+ } from "@aws-sdk/client-s3";
7
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
8
+ import { OrganizationsClient } from "@aws-sdk/client-organizations";
9
+ import { SSOAdminClient } from "@aws-sdk/client-sso-admin";
10
+ import { IdentitystoreClient } from "@aws-sdk/client-identitystore";
11
+ import { AccountClient } from "@aws-sdk/client-account";
12
+ import * as v from "valibot";
13
+ import { operationSchema } from "../operations.js";
14
+ import {
15
+ stateSchema,
16
+ createWorkingState,
17
+ materializeWorkingState
18
+ } from "../state.js";
19
+ import { scanOrganization, scanIdentityCenter } from "../scanLogic.js";
20
+ import { executeOperation } from "../applyLogic.js";
21
+ import { assertUnreachable } from "../helpers.js";
22
+ const scanRequestSchema = v.strictObject({
23
+ action: v.literal("scan")
24
+ });
25
+ const getStateUrlRequestSchema = v.strictObject({
26
+ action: v.literal("getStateUrl")
27
+ });
28
+ const applyRequestSchema = v.strictObject({
29
+ action: v.literal("apply"),
30
+ operations: v.pipe(v.array(operationSchema), v.minLength(1)),
31
+ allowDestructive: v.boolean()
32
+ });
33
+ const lambdaRequestSchema = v.variant("action", [
34
+ scanRequestSchema,
35
+ getStateUrlRequestSchema,
36
+ applyRequestSchema
37
+ ]);
38
+ const scanResponseSchema = v.strictObject({
39
+ action: v.literal("scan"),
40
+ success: v.literal(true),
41
+ summary: v.strictObject({
42
+ organizationalUnits: v.number(),
43
+ accounts: v.number(),
44
+ users: v.number(),
45
+ groups: v.number(),
46
+ permissionSets: v.number(),
47
+ accountAssignments: v.number()
48
+ }),
49
+ state: stateSchema
50
+ });
51
+ const getStateUrlResponseSchema = v.strictObject({
52
+ action: v.literal("getStateUrl"),
53
+ success: v.literal(true),
54
+ url: v.string(),
55
+ expiresInSeconds: v.number()
56
+ });
57
+ const applySuccessResponseSchema = v.strictObject({
58
+ action: v.literal("apply"),
59
+ success: v.literal(true),
60
+ operationsCompleted: v.number(),
61
+ state: stateSchema
62
+ });
63
+ const errorResponseSchema = v.strictObject({
64
+ success: v.literal(false),
65
+ error: v.strictObject({
66
+ kind: v.picklist([
67
+ "validation",
68
+ "concurrencyConflict",
69
+ "operationFailed",
70
+ "internal"
71
+ ]),
72
+ message: v.string(),
73
+ details: v.optional(
74
+ v.strictObject({
75
+ failedOperation: v.optional(v.number()),
76
+ operationsCompleted: v.optional(v.number()),
77
+ partialState: v.optional(stateSchema),
78
+ validationIssues: v.optional(v.array(v.string()))
79
+ })
80
+ )
81
+ })
82
+ });
83
+ const lambdaResponseSchema = v.union([
84
+ scanResponseSchema,
85
+ getStateUrlResponseSchema,
86
+ applySuccessResponseSchema,
87
+ errorResponseSchema
88
+ ]);
89
+ const STATE_KEY = "state.json";
90
+ const PRESIGNED_URL_EXPIRY_SECONDS = 3600;
91
+ const RUNTIME_DEFAULTS = {
92
+ createAccount: {
93
+ timeoutInMs: 3e5,
94
+ pollIntervalInMs: 5e3
95
+ },
96
+ accountAssignment: {
97
+ timeoutInMs: 6e4,
98
+ pollIntervalInMs: 2e3
99
+ },
100
+ permissionSetProvisioning: {
101
+ timeoutInMs: 6e4,
102
+ pollIntervalInMs: 2e3
103
+ }
104
+ };
105
+ const lambdaLogger = {
106
+ log: (...args) => console.log(...args),
107
+ info: (...args) => console.info(...args),
108
+ warn: (...args) => console.warn(...args),
109
+ error: (...args) => console.error(...args),
110
+ debug: (...args) => console.debug(...args),
111
+ trace: (...args) => console.trace(...args)
112
+ };
113
+ const s3Client = new S3Client({});
114
+ const organizationsClient = new OrganizationsClient({});
115
+ const ssoAdminClient = new SSOAdminClient({});
116
+ const identityStoreClient = new IdentitystoreClient({});
117
+ const accountClient = new AccountClient({});
118
+ async function handler(event) {
119
+ try {
120
+ const parseResult = v.safeParse(lambdaRequestSchema, event);
121
+ if (!parseResult.success) {
122
+ const issues = parseResult.issues.map(
123
+ (issue) => `${issue.path?.map((p) => p.key).join(".") ?? "root"}: ${issue.message}`
124
+ );
125
+ const response = buildErrorResponse(
126
+ "validation",
127
+ "Invalid request payload.",
128
+ { validationIssues: issues }
129
+ );
130
+ return validateResponse(response);
131
+ }
132
+ const request = parseResult.output;
133
+ const bucket = process.env.STATE_BUCKET_NAME;
134
+ if (bucket == null || bucket.length === 0) {
135
+ const response = buildErrorResponse(
136
+ "internal",
137
+ "STATE_BUCKET_NAME environment variable is not configured."
138
+ );
139
+ return validateResponse(response);
140
+ }
141
+ if (request.action === "scan") {
142
+ const response = await handleScan({ s3Client, bucket, organizationsClient, ssoAdminClient, identityStoreClient });
143
+ return validateResponse(response);
144
+ }
145
+ if (request.action === "getStateUrl") {
146
+ const response = await handleGetStateUrl({ s3Client, bucket });
147
+ return validateResponse(response);
148
+ }
149
+ if (request.action === "apply") {
150
+ const response = await handleApply({
151
+ s3Client,
152
+ bucket,
153
+ operations: request.operations,
154
+ allowDestructive: request.allowDestructive,
155
+ organizationsClient,
156
+ ssoAdminClient,
157
+ identityStoreClient,
158
+ accountClient
159
+ });
160
+ return validateResponse(response);
161
+ }
162
+ assertUnreachable(request, "Unsupported action in handler.");
163
+ } catch (error) {
164
+ const message = error instanceof Error ? error.message : "An unexpected error occurred.";
165
+ const response = buildErrorResponse("internal", message);
166
+ return validateResponse(response);
167
+ }
168
+ }
169
+ function buildErrorResponse(kind, message, details) {
170
+ return {
171
+ success: false,
172
+ error: {
173
+ kind,
174
+ message,
175
+ ...details != null ? { details } : {}
176
+ }
177
+ };
178
+ }
179
+ function validateResponse(response) {
180
+ const result = v.safeParse(lambdaResponseSchema, response);
181
+ if (!result.success) {
182
+ return {
183
+ success: false,
184
+ error: {
185
+ kind: "internal",
186
+ message: "Response validation failed before returning.",
187
+ details: {
188
+ validationIssues: result.issues.map(
189
+ (issue) => `${issue.path?.map((p) => p.key).join(".") ?? "root"}: ${issue.message}`
190
+ )
191
+ }
192
+ }
193
+ };
194
+ }
195
+ return result.output;
196
+ }
197
+ async function readStateFromS3(props) {
198
+ const response = await props.s3Client.send(
199
+ new GetObjectCommand({
200
+ Bucket: props.bucket,
201
+ Key: STATE_KEY
202
+ })
203
+ );
204
+ const body = await response.Body?.transformToString();
205
+ if (body == null) {
206
+ throw new Error("State not found. Run remote scan first.");
207
+ }
208
+ const parsed = JSON.parse(body);
209
+ const state = v.parse(stateSchema, parsed);
210
+ const etag = response.ETag ?? "";
211
+ return { state, etag };
212
+ }
213
+ async function writeStateToS3(props) {
214
+ await props.s3Client.send(new PutObjectCommand({
215
+ Bucket: props.bucket,
216
+ Key: STATE_KEY,
217
+ Body: JSON.stringify(props.state, null, 2),
218
+ ContentType: "application/json",
219
+ IfMatch: props.ifMatch
220
+ }));
221
+ }
222
+ function isS3PreconditionFailed(error) {
223
+ if (error instanceof S3ServiceException) {
224
+ return error.name === "PreconditionFailed" || error.$metadata?.httpStatusCode === 412;
225
+ }
226
+ return false;
227
+ }
228
+ async function handleScan(props) {
229
+ const identityCenterInstanceArn = process.env.IDENTITY_CENTER_INSTANCE_ARN || void 0;
230
+ const [organization, identityCenter] = await Promise.all([
231
+ scanOrganization({ organizationsClient: props.organizationsClient }),
232
+ scanIdentityCenter({
233
+ ssoAdminClient: props.ssoAdminClient,
234
+ identityStoreClient: props.identityStoreClient,
235
+ requestedInstanceArn: identityCenterInstanceArn
236
+ })
237
+ ]);
238
+ const state = {
239
+ version: "1",
240
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
241
+ organization,
242
+ identityCenter
243
+ };
244
+ await writeStateToS3({
245
+ s3Client: props.s3Client,
246
+ bucket: props.bucket,
247
+ state
248
+ });
249
+ return {
250
+ action: "scan",
251
+ success: true,
252
+ summary: {
253
+ organizationalUnits: state.organization.organizationalUnits.length,
254
+ accounts: state.organization.accounts.length,
255
+ users: state.identityCenter.users.length,
256
+ groups: state.identityCenter.groups.length,
257
+ permissionSets: state.identityCenter.permissionSets.length,
258
+ accountAssignments: state.identityCenter.accountAssignments.length
259
+ },
260
+ state
261
+ };
262
+ }
263
+ async function handleGetStateUrl(props) {
264
+ const command = new GetObjectCommand({
265
+ Bucket: props.bucket,
266
+ Key: STATE_KEY
267
+ });
268
+ const url = await getSignedUrl(props.s3Client, command, {
269
+ expiresIn: PRESIGNED_URL_EXPIRY_SECONDS
270
+ });
271
+ return {
272
+ action: "getStateUrl",
273
+ success: true,
274
+ url,
275
+ expiresInSeconds: PRESIGNED_URL_EXPIRY_SECONDS
276
+ };
277
+ }
278
+ async function handleApply(props) {
279
+ const stateResult = await loadStateForApply({
280
+ s3Client: props.s3Client,
281
+ bucket: props.bucket
282
+ });
283
+ if (!stateResult.ok) {
284
+ return stateResult.response;
285
+ }
286
+ const { state: currentState, etag } = stateResult;
287
+ let workingState = createWorkingState({ state: currentState });
288
+ let operationsCompleted = 0;
289
+ for (let i = 0; i < props.operations.length; i++) {
290
+ const operation = props.operations[i];
291
+ try {
292
+ workingState = await executeOperation({
293
+ state: workingState,
294
+ organizationsClient: props.organizationsClient,
295
+ accountClient: props.accountClient,
296
+ ssoAdminClient: props.ssoAdminClient,
297
+ identityStoreClient: props.identityStoreClient,
298
+ logger: lambdaLogger,
299
+ context: {
300
+ organization: {
301
+ rootId: workingState.organization.rootId
302
+ }
303
+ },
304
+ runtime: RUNTIME_DEFAULTS,
305
+ operation
306
+ });
307
+ operationsCompleted++;
308
+ } catch (error) {
309
+ const partialState = materializeWorkingState({ workingState });
310
+ try {
311
+ await writeStateToS3({
312
+ s3Client: props.s3Client,
313
+ bucket: props.bucket,
314
+ state: partialState,
315
+ ifMatch: etag
316
+ });
317
+ } catch (writeError) {
318
+ if (isS3PreconditionFailed(writeError)) {
319
+ return buildErrorResponse(
320
+ "concurrencyConflict",
321
+ "Concurrent state modification detected while writing partial state."
322
+ );
323
+ }
324
+ lambdaLogger.error(
325
+ "Failed to write partial state after operation failure:",
326
+ writeError
327
+ );
328
+ }
329
+ const errorMessage = error instanceof Error ? error.message : "Unknown operation error";
330
+ return buildErrorResponse("operationFailed", errorMessage, {
331
+ failedOperation: i,
332
+ operationsCompleted,
333
+ partialState
334
+ });
335
+ }
336
+ }
337
+ const finalState = materializeWorkingState({ workingState });
338
+ try {
339
+ await writeStateToS3({
340
+ s3Client: props.s3Client,
341
+ bucket: props.bucket,
342
+ state: finalState,
343
+ ifMatch: etag
344
+ });
345
+ } catch (error) {
346
+ if (isS3PreconditionFailed(error)) {
347
+ return buildErrorResponse(
348
+ "concurrencyConflict",
349
+ "Concurrent state modification detected. Another apply may have completed while this one was running."
350
+ );
351
+ }
352
+ throw error;
353
+ }
354
+ return {
355
+ action: "apply",
356
+ success: true,
357
+ operationsCompleted,
358
+ state: finalState
359
+ };
360
+ }
361
+ async function loadStateForApply(props) {
362
+ try {
363
+ const result = await readStateFromS3({
364
+ s3Client: props.s3Client,
365
+ bucket: props.bucket
366
+ });
367
+ return { ok: true, state: result.state, etag: result.etag };
368
+ } catch (error) {
369
+ const message = error instanceof Error ? error.message : "Failed to read state from S3.";
370
+ return { ok: false, response: buildErrorResponse("internal", message) };
371
+ }
372
+ }
373
+ export {
374
+ handler
375
+ };
@@ -0,0 +1,220 @@
1
+ import {
2
+ InvokeCommand,
3
+ TooManyRequestsException
4
+ } from "@aws-sdk/client-lambda";
5
+ import * as v from "valibot";
6
+ import { assertUnreachable } from "./helpers.js";
7
+ import { operationSchema } from "./operations.js";
8
+ import { stateSchema } from "./state.js";
9
+ const scanRequestSchema = v.strictObject({
10
+ action: v.literal("scan")
11
+ });
12
+ const getStateUrlRequestSchema = v.strictObject({
13
+ action: v.literal("getStateUrl")
14
+ });
15
+ const applyRequestSchema = v.strictObject({
16
+ action: v.literal("apply"),
17
+ operations: v.pipe(v.array(operationSchema), v.minLength(1)),
18
+ allowDestructive: v.boolean()
19
+ });
20
+ const lambdaRequestSchema = v.variant("action", [
21
+ scanRequestSchema,
22
+ getStateUrlRequestSchema,
23
+ applyRequestSchema
24
+ ]);
25
+ const scanResponseSchema = v.strictObject({
26
+ action: v.literal("scan"),
27
+ success: v.literal(true),
28
+ summary: v.strictObject({
29
+ organizationalUnits: v.number(),
30
+ accounts: v.number(),
31
+ users: v.number(),
32
+ groups: v.number(),
33
+ permissionSets: v.number(),
34
+ accountAssignments: v.number()
35
+ }),
36
+ state: stateSchema
37
+ });
38
+ const getStateUrlResponseSchema = v.strictObject({
39
+ action: v.literal("getStateUrl"),
40
+ success: v.literal(true),
41
+ url: v.string(),
42
+ expiresInSeconds: v.number()
43
+ });
44
+ const applySuccessResponseSchema = v.strictObject({
45
+ action: v.literal("apply"),
46
+ success: v.literal(true),
47
+ operationsCompleted: v.number(),
48
+ state: stateSchema
49
+ });
50
+ const errorResponseSchema = v.strictObject({
51
+ success: v.literal(false),
52
+ error: v.strictObject({
53
+ kind: v.picklist([
54
+ "validation",
55
+ "concurrencyConflict",
56
+ "operationFailed",
57
+ "internal"
58
+ ]),
59
+ message: v.string(),
60
+ details: v.optional(
61
+ v.strictObject({
62
+ failedOperation: v.optional(v.number()),
63
+ operationsCompleted: v.optional(v.number()),
64
+ partialState: v.optional(stateSchema),
65
+ validationIssues: v.optional(v.array(v.string()))
66
+ })
67
+ )
68
+ })
69
+ });
70
+ const lambdaResponseSchema = v.union([
71
+ scanResponseSchema,
72
+ getStateUrlResponseSchema,
73
+ applySuccessResponseSchema,
74
+ errorResponseSchema
75
+ ]);
76
+ async function invokeLambdaCommand(lambdaClient, lambdaArn, payload) {
77
+ try {
78
+ const response = await lambdaClient.send(
79
+ new InvokeCommand({
80
+ FunctionName: lambdaArn,
81
+ InvocationType: "RequestResponse",
82
+ Payload: new TextEncoder().encode(JSON.stringify(payload))
83
+ })
84
+ );
85
+ return { ok: true, response };
86
+ } catch (error) {
87
+ if (error instanceof TooManyRequestsException) {
88
+ return {
89
+ ok: false,
90
+ error: {
91
+ kind: "concurrencyConflict",
92
+ message: "Lambda concurrency limit reached. Another operation may be in progress."
93
+ }
94
+ };
95
+ }
96
+ const message = error instanceof Error ? error.message : "Unknown invocation error";
97
+ return {
98
+ ok: false,
99
+ error: { kind: "invocationError", message }
100
+ };
101
+ }
102
+ }
103
+ function parseResponsePayload(payload) {
104
+ try {
105
+ const responseText = new TextDecoder().decode(payload);
106
+ return { ok: true, value: JSON.parse(responseText) };
107
+ } catch {
108
+ return { ok: false };
109
+ }
110
+ }
111
+ async function invokeLambda(props) {
112
+ const invokeResult = await invokeLambdaCommand(props.lambdaClient, props.lambdaArn, props.payload);
113
+ if (!invokeResult.ok) return invokeResult;
114
+ const rawResponse = invokeResult.response;
115
+ if (rawResponse.FunctionError) {
116
+ const errorPayload = rawResponse.Payload ? new TextDecoder().decode(rawResponse.Payload) : "Lambda function execution failed";
117
+ return {
118
+ ok: false,
119
+ error: { kind: "invocationError", message: errorPayload }
120
+ };
121
+ }
122
+ if (!rawResponse.Payload) {
123
+ return {
124
+ ok: false,
125
+ error: { kind: "invocationError", message: "Empty response payload" }
126
+ };
127
+ }
128
+ const parsed = parseResponsePayload(rawResponse.Payload);
129
+ if (!parsed.ok) {
130
+ return {
131
+ ok: false,
132
+ error: {
133
+ kind: "invocationError",
134
+ message: "Failed to parse Lambda response as JSON"
135
+ }
136
+ };
137
+ }
138
+ const result = v.safeParse(lambdaResponseSchema, parsed.value);
139
+ if (!result.success) {
140
+ const issues = result.issues.map((issue) => `${issue.path?.map((p) => p.key).join(".") ?? "root"}: ${issue.message}`).join("; ");
141
+ return {
142
+ ok: false,
143
+ error: {
144
+ kind: "validation",
145
+ details: `Lambda response validation failed: ${issues}`
146
+ }
147
+ };
148
+ }
149
+ const response = result.output;
150
+ if ("success" in response && response.success === false) {
151
+ const errorKind = response.error.kind;
152
+ if (errorKind === "validation") {
153
+ return {
154
+ ok: false,
155
+ error: {
156
+ kind: "validation",
157
+ details: response.error.message
158
+ }
159
+ };
160
+ }
161
+ if (errorKind === "concurrencyConflict") {
162
+ return {
163
+ ok: false,
164
+ error: {
165
+ kind: "concurrencyConflict",
166
+ message: response.error.message
167
+ }
168
+ };
169
+ }
170
+ if (errorKind === "operationFailed") {
171
+ return {
172
+ ok: false,
173
+ error: {
174
+ kind: "operationFailed",
175
+ failedOperation: response.error.details?.failedOperation ?? 0,
176
+ totalOperations: (response.error.details?.operationsCompleted ?? 0) + 1,
177
+ error: response.error.message,
178
+ partialState: response.error.details?.partialState ?? buildEmptyStateForError()
179
+ }
180
+ };
181
+ }
182
+ if (errorKind === "internal") {
183
+ return {
184
+ ok: false,
185
+ error: {
186
+ kind: "invocationError",
187
+ message: response.error.message
188
+ }
189
+ };
190
+ }
191
+ assertUnreachable(errorKind, "Unsupported error kind in Lambda response.");
192
+ }
193
+ return { ok: true, response };
194
+ }
195
+ function buildEmptyStateForError() {
196
+ return {
197
+ version: "1",
198
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
199
+ organization: {
200
+ rootId: "",
201
+ organizationalUnits: [],
202
+ accounts: []
203
+ },
204
+ identityCenter: {
205
+ instanceArn: "",
206
+ identityStoreId: "",
207
+ users: [],
208
+ groups: [],
209
+ groupMemberships: [],
210
+ permissionSets: [],
211
+ accountAssignments: [],
212
+ accessRoles: []
213
+ }
214
+ };
215
+ }
216
+ export {
217
+ invokeLambda,
218
+ lambdaRequestSchema,
219
+ lambdaResponseSchema
220
+ };
package/dist/logger.js ADDED
@@ -0,0 +1,26 @@
1
+ const consoleLogger = {
2
+ log: (...args) => console.log(...args),
3
+ info: (...args) => console.info(...args),
4
+ warn: (...args) => console.warn(...args),
5
+ error: (...args) => console.error(...args),
6
+ debug: (...args) => console.debug(...args),
7
+ trace: (...args) => console.trace(...args)
8
+ };
9
+ const noopLogger = {
10
+ log: () => {
11
+ },
12
+ info: () => {
13
+ },
14
+ warn: () => {
15
+ },
16
+ error: () => {
17
+ },
18
+ debug: () => {
19
+ },
20
+ trace: () => {
21
+ }
22
+ };
23
+ export {
24
+ consoleLogger,
25
+ noopLogger
26
+ };