diff --git a/.projenrc.ts b/.projenrc.ts index 4a621faa2..c13731041 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -600,10 +600,8 @@ const cloudAssemblyApi = configureProject( srcdir: 'lib', devDeps: [ cloudAssemblySchema.customizeReference({ versionType: 'exact' }), - '@types/json-source-map@^0.6.0', ], deps: [ - 'json-source-map@^0.6.1', 'jsonschema@^1.5.0', 'semver', ], diff --git a/packages/@aws-cdk/cloud-assembly-api/.projen/deps.json b/packages/@aws-cdk/cloud-assembly-api/.projen/deps.json index b1364712e..f20fcf952 100644 --- a/packages/@aws-cdk/cloud-assembly-api/.projen/deps.json +++ b/packages/@aws-cdk/cloud-assembly-api/.projen/deps.json @@ -13,11 +13,6 @@ "name": "@types/jest", "type": "build" }, - { - "name": "@types/json-source-map", - "version": "^0.6.0", - "type": "build" - }, { "name": "@types/node", "version": "^20", @@ -111,11 +106,6 @@ "name": "@aws-cdk/cloud-assembly-schema", "type": "peer" }, - { - "name": "json-source-map", - "version": "^0.6.1", - "type": "runtime" - }, { "name": "jsonschema", "version": "^1.5.0", diff --git a/packages/@aws-cdk/cloud-assembly-api/lib/construct-tree.ts b/packages/@aws-cdk/cloud-assembly-api/lib/construct-tree.ts deleted file mode 100644 index 12b6f07ec..000000000 --- a/packages/@aws-cdk/cloud-assembly-api/lib/construct-tree.ts +++ /dev/null @@ -1,422 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import { ArtifactMetadataEntryType, CFN_RESOURCE_PROPS_ATTRIBUTE, CFN_RESOURCE_TYPE_ATTRIBUTE, type MetadataEntry } from '@aws-cdk/cloud-assembly-schema'; -import type { CloudFormationStackArtifact } from './artifacts/cloudformation-artifact'; -import { ASSET_RESOURCE_METADATA_PATH_KEY } from './assets'; -import type { CloudAssembly } from './cloud-assembly'; - -/** Construct ids that aws-cdk-lib injects into the tree but aren't user constructs. */ -const CDK_INTERNAL_IDS = new Set(['Tree', 'CDKMetadata', 'BootstrapVersion', 'CheckBootstrapVersion']); - -/** - * A construct from tree.json joined with the CloudFormation metadata of the - * stack it belongs to: the construct path/id plus, for CFN resources, the - * logical ID and resource type. Readers that need more (for example a source - * location) extend this and build the richer tree via {@link buildConstructTree}. - */ -export interface ConstructTreeNode { - readonly path: string; - readonly id: string; - /** CFN resource type (e.g. "AWS::S3::Bucket"), if this construct is a CFN resource. */ - readonly type?: string; - /** CFN logical ID, if this construct maps to a CFN resource. */ - readonly logicalId?: string; - /** - * Absolute path to the `*.template.json` that declares this construct's CFN - * resource -- the nested template for resources inside a NestedStack. Set - * only for CFN resources whose template is resolvable. - */ - readonly templateFile?: string; - /** - * Resolved CloudFormation resource properties (the synthesized values from - * the `aws:cdk:cloudformation:props` attribute), if this is a CFN resource. - */ - readonly cfnProperties?: Record; - readonly children: readonly ConstructTreeNode[]; -} - -/** - * The generic fields {@link buildConstructTree} computes for each node before a - * consumer decorates it. `children` are already the decorated node type. - */ -export interface ConstructTreeNodeFields { - readonly path: string; - readonly id: string; - readonly type?: string; - readonly logicalId?: string; - readonly templateFile?: string; - readonly cfnProperties?: Record; - readonly children: readonly T[]; -} - -/** - * Produces a node of type `T` from the generic fields, the owning stack - * artifact (if any), and the construct path. Readers use the stack + path to - * attach extra data (for example a source location traced from the stack). - */ -export type ConstructNodeDecorator = ( - fields: ConstructTreeNodeFields, - stack: CloudFormationStackArtifact | undefined, - constructPath: string, -) => T; - -/** - * Async variant of {@link ConstructNodeDecorator}: the decorator may return a - * `Promise`, so it can do non-blocking I/O (for example reading source maps - * off disk with `fs.promises`) while building each node. Used by - * {@link buildConstructTreeAsync}. - */ -export type AsyncConstructNodeDecorator = ( - fields: ConstructTreeNodeFields, - stack: CloudFormationStackArtifact | undefined, - constructPath: string, -) => T | Promise; - -/** - * A pre-built index over a construct tree. Walks the tree once and exposes - * O(1) path lookup plus pre-order iteration over every node, so callers never - * re-implement the recursive descent over `children`. - * - * Generic over the concrete node type, so callers that store richer nodes get - * those richer nodes back from `byPath()` and iteration. - */ -export class ConstructIndex implements Iterable { - /** Build an index from the roots of a construct tree. */ - public static fromTree(tree: readonly T[]): ConstructIndex { - const byPath = new Map(); - // Pre-order insertion: iteration order below relies on the Map preserving it. - visit(tree, (node) => byPath.set(node.path, node)); - return new ConstructIndex(byPath); - } - - private constructor(private readonly byPathMap: Map) { - } - - /** The node at a given construct path, or undefined if absent. */ - public byPath(constructPath: string): T | undefined { - return this.byPathMap.get(constructPath); - } - - /** Number of nodes in the tree. */ - public get size(): number { - return this.byPathMap.size; - } - - /** Pre-order iteration over every node in the tree. */ - public [Symbol.iterator](): Iterator { - return this.byPathMap.values(); - } -} - -/** The single recursive descent over a construct tree. */ -function visit(nodes: readonly T[], fn: (node: T) => void): void { - for (const node of nodes) { - fn(node); - // In a homogeneous tree the children are the same concrete node type. - if (node.children.length > 0) visit(node.children as readonly T[], fn); - } -} - -/** - * Read a cloud assembly's tree.json and join it with each stack's manifest - * metadata to produce a construct tree where every CFN resource carries its - * logicalId and CFN type. - * - * Supports: - * - Flat assemblies (stacks directly under App). - * - Stage-based apps: each Stage produces a nested cloud-assembly artifact - * under assembly-STAGE/. stacksRecursively descends into them. - * - NestedStack contents: aws-cdk-lib emits the per-resource metadata for - * nested-stack constructs into the PARENT stack's artifact metadata, - * keyed by full construct path. - * - * The `decorate` callback turns each node's generic fields and its metadata - * entries into the concrete node type (e.g. attaching a resolved source location). - * - * This is the synchronous variant: `decorate` must return `T`. Callers whose - * decoration needs non-blocking I/O should use {@link buildConstructTreeAsync}. - */ -export function buildConstructTree( - assembly: CloudAssembly, - decorate: ConstructNodeDecorator, -): T[] { - const roots = readRoots(assembly); - if (!roots) return []; - const { rawRoots, ctx } = roots; - return rawRoots.map((child) => buildNodeSync(child, ctx, undefined, NO_TEMPLATE, decorate)); -} - -/** - * Async counterpart of {@link buildConstructTree}: the `decorate` callback may - * return a `Promise`, letting it do non-blocking I/O per node (for example - * tracing source locations with `fs.promises`) without blocking the calling - * thread. Behaves identically otherwise; the tree is walked in pre-order and - * each node's children are resolved before the node is decorated. - */ -export async function buildConstructTreeAsync( - assembly: CloudAssembly, - decorate: AsyncConstructNodeDecorator, -): Promise { - const roots = readRoots(assembly); - if (!roots) return []; - const { rawRoots, ctx } = roots; - const out: T[] = []; - for (const child of rawRoots) { - out.push(await buildNodeAsync(child, ctx, undefined, NO_TEMPLATE, decorate)); - } - return out; -} - -/** - * Load tree.json and build the walk context shared by both tree builders. - * Returns the user-facing root raw nodes (cdk-internal ids filtered out) plus - * the context, or undefined when there's no tree to walk. - */ -function readRoots(assembly: CloudAssembly): WalkRoots | undefined { - const treeArtifact = assembly.tree(); - if (!treeArtifact) return undefined; - - const rawTree = loadTree(path.join(assembly.directory, treeArtifact.file)); - if (!rawTree) return undefined; - - const ctx: WalkContext = { - stackIndex: buildStackIndex(assembly.stacksRecursively), - templateCache: new Map(), - }; - const rawRoots = Object.values(rawTree.children ?? {}).filter((child) => !isCdkInternal(child.id)); - return { rawRoots, ctx }; -} - -/** The user-facing root raw nodes plus the shared walk context. */ -interface WalkRoots { - readonly rawRoots: RawTreeNode[]; - readonly ctx: WalkContext; -} - -/** Shared state for a single tree walk (decorator-independent). */ -interface WalkContext { - readonly stackIndex: StackMetadataIndex; - /** Parsed nested templates, cached by absolute path. */ - readonly templateCache: Map; -} - -/** The slice of a CloudFormation template the tree walk reads. */ -interface CfnTemplate { - readonly Resources?: Record; -} -interface CfnResource { - readonly Type?: string; - readonly Metadata?: Record; -} - -/** - * The active CloudFormation template threaded down the tree walk: a resolved - * `{ file, template }` pair, or both `undefined` when there's no resolvable - * template (the root, or a nested stack that isn't CDK-resolvable). Never a - * partial mix. - */ -type TemplateScope = - | { readonly file: string; readonly template: CfnTemplate } - | { readonly file: undefined; readonly template: undefined }; - -/** Scope for nodes with no resolvable template: the root seed and unresolvable nested stacks. */ -const NO_TEMPLATE: TemplateScope = { file: undefined, template: undefined }; - -/** - * Raw tree.json node. The root tree.json holds the FULL hierarchy including - * Stage children; nested-assembly subdirs don't shard tree.json. - */ -interface RawTreeNode { - readonly id: string; - readonly path: string; - readonly children?: { [key: string]: RawTreeNode }; - readonly attributes?: { [key: string]: unknown }; -} - -/** A stack artifact plus its metadata Map, keyed (in the index) by construct path. */ -interface StackMetadata { - readonly stack: CloudFormationStackArtifact; - readonly metadata: Map; -} -type StackMetadataIndex = Map; - -/** The generic fields for one node, minus its (decorated) children. */ -type NodeFieldsBase = Omit, 'children'>; - -/** A child to recurse into, paired with the template scope it inherits. */ -interface ChildInput { - readonly raw: RawTreeNode; - readonly scope: TemplateScope; -} - -/** - * Everything {@link buildNodeSync}/{@link buildNodeAsync} need for one node, - * computed once (no I/O beyond the cached template reads), so the two builders - * differ only in how they recurse and invoke `decorate`. - */ -interface PreparedNode { - readonly base: NodeFieldsBase; - /** Owning stack metadata: its `stack` is the decorate arg and the children's inheritedStack. */ - readonly owner: StackMetadata | undefined; - readonly constructPath: string; - readonly childInputs: readonly ChildInput[]; -} - -/** Reads and parses tree.json. Kept synchronous on purpose; see {@link loadTemplate}. */ -function loadTree(treePath: string): RawTreeNode | undefined { - if (!fs.existsSync(treePath)) return undefined; - const content = JSON.parse(fs.readFileSync(treePath, 'utf-8')); - return content.tree; -} - -/** - * Index a stack's metadata by its tree-path key. Uses hierarchicalId, which - * resolves to stack.node.path for everything aws-cdk-lib emits. Caches the - * metadata Map because stack.metadata re-reads from disk on each access. - */ -function buildStackIndex(stacks: CloudFormationStackArtifact[]): StackMetadataIndex { - const index: StackMetadataIndex = new Map(); - for (const stack of stacks) { - index.set(stack.hierarchicalId, { stack, metadata: new Map(Object.entries(stack.metadata)) }); - } - return index; -} - -/** - * Compute the generic fields, owning stack, and per-child template scopes for a - * single node. Owns all of the tree/metadata/template join logic; the sync and - * async builders share it so the substantive walk lives in exactly one place. - */ -function prepareNode( - raw: RawTreeNode, - ctx: WalkContext, - inheritedStack: StackMetadata | undefined, - inherited: TemplateScope, -): PreparedNode { - const stackHere = ctx.stackIndex.get(raw.path); - const owner = stackHere ?? inheritedStack; - // A stack node switches to its own template via the artifact's cached getter, - // which throws on a missing/corrupt top-level template -- a real assembly error - // we surface rather than swallow. Other nodes inherit the active scope. - const scope: TemplateScope = stackHere - ? { file: stackHere.stack.templateFullPath, template: stackHere.stack.template as CfnTemplate } - : inherited; - - // Metadata keys carry a leading "/", construct paths in tree.json don't. - const entries = owner?.metadata.get('/' + raw.path) ?? []; - const logicalId = logicalIdFromEntries(entries); - - const cfnTypeRaw = raw.attributes?.[CFN_RESOURCE_TYPE_ATTRIBUTE]; - const cfnType = typeof cfnTypeRaw === 'string' ? cfnTypeRaw : undefined; - const cfnPropsRaw = raw.attributes?.[CFN_RESOURCE_PROPS_ATTRIBUTE]; - const cfnProperties = - typeof cfnPropsRaw === 'object' && cfnPropsRaw !== null ? (cfnPropsRaw as Record) : undefined; - - // A NestedStack switches its subtree to the nested scope; other nodes inherit. - const childInputs: ChildInput[] = Object.values(raw.children ?? {}) - .filter((child) => !isCdkInternal(child.id)) - .map((child) => ({ raw: child, scope: nestedBoundary(child, raw, owner, scope.template, ctx) ?? scope })); - - // Only CFN resources (those with a logical ID) carry a templateFile. - const nodeTemplateFile = logicalId !== undefined ? scope.file : undefined; - return { - base: { path: raw.path, id: raw.id, type: cfnType, logicalId, templateFile: nodeTemplateFile, cfnProperties }, - owner, - constructPath: raw.path, - childInputs, - }; -} - -function buildNodeSync( - raw: RawTreeNode, - ctx: WalkContext, - inheritedStack: StackMetadata | undefined, - inherited: TemplateScope, - decorate: ConstructNodeDecorator, -): T { - const prepared = prepareNode(raw, ctx, inheritedStack, inherited); - const children = prepared.childInputs.map( - (ci) => buildNodeSync(ci.raw, ctx, prepared.owner, ci.scope, decorate), - ); - return decorate({ ...prepared.base, children }, prepared.owner?.stack, prepared.constructPath); -} - -async function buildNodeAsync( - raw: RawTreeNode, - ctx: WalkContext, - inheritedStack: StackMetadata | undefined, - inherited: TemplateScope, - decorate: AsyncConstructNodeDecorator, -): Promise { - const prepared = prepareNode(raw, ctx, inheritedStack, inherited); - const children: T[] = []; - for (const ci of prepared.childInputs) { - children.push(await buildNodeAsync(ci.raw, ctx, prepared.owner, ci.scope, decorate)); - } - return decorate({ ...prepared.base, children }, prepared.owner?.stack, prepared.constructPath); -} - -function logicalIdFromEntries(entries: MetadataEntry[]): string | undefined { - const entry = entries.find((e) => e.type === ArtifactMetadataEntryType.LOGICAL_ID); - return typeof entry?.data === 'string' ? entry.data : undefined; -} - -/** - * Parses a template file (cached by absolute path); undefined when missing/unparseable. - * - * Synchronous by design: {@link prepareNode} is shared by the sync `buildConstructTree` - * and the async `buildConstructTreeAsync`, so it cannot await. These reads are O(stacks) - * (one tree.json plus a handful of nested templates), not the per-construct hot path, so - * blocking here is acceptable. Do not switch to `fs.promises` without splitting the shared walk. - */ -function loadTemplate(absPath: string, cache: Map): CfnTemplate | undefined { - if (cache.has(absPath)) return cache.get(absPath); - let template: CfnTemplate | undefined; - try { - template = JSON.parse(fs.readFileSync(absPath, 'utf-8')) as CfnTemplate; - } catch { - template = undefined; // missing/unparseable: that subtree just won't resolve a templateFile - } - cache.set(absPath, template); - return template; -} - -/** - * If `child` is a NestedStack, resolves the template scope for its subtree; - * otherwise returns `undefined` (caller keeps the active scope). - * - * Detection keys off the sibling `AWS::CloudFormation::Stack` that aws-cdk-lib - * emits at `/.NestedStack/.NestedStackResource` (whose - * `aws:asset:path` points at the nested template), NOT the construct's fqn -- a - * jsii-published NestedStack subclass has an fqn that doesn't end in - * ".NestedStack", but the sibling is named the same regardless. Returns - * NO_TEMPLATE (not the parent's) when unresolvable, since the nested stack's - * resources don't live in the parent template. - */ -function nestedBoundary( - child: RawTreeNode, - parent: RawTreeNode, - owner: StackMetadata | undefined, - currentTemplate: CfnTemplate | undefined, - ctx: WalkContext, -): TemplateScope | undefined { - const resourceNode = parent.children?.[`${child.id}.NestedStack`]?.children?.[`${child.id}.NestedStackResource`]; - // No sibling -> not a nested stack. A real nested stack always lives under a - // stack, so `owner` is defined here; the check also narrows it. - if (!resourceNode || !owner) return undefined; - - const logicalId = logicalIdFromEntries(owner.metadata.get('/' + resourceNode.path) ?? []); - const assetPath = logicalId !== undefined - ? currentTemplate?.Resources?.[logicalId]?.Metadata?.[ASSET_RESOURCE_METADATA_PATH_KEY] - : undefined; - if (typeof assetPath !== 'string') return NO_TEMPLATE; - // Asset path is relative to the OWNER stack's assembly dir (the Stage - // sub-assembly for staged stacks), not necessarily the root assembly. - const file = path.join(path.dirname(owner.stack.templateFullPath), assetPath); - const template = loadTemplate(file, ctx.templateCache); - return template ? { file, template } : NO_TEMPLATE; -} - -function isCdkInternal(id: string): boolean { - return CDK_INTERNAL_IDS.has(id); -} diff --git a/packages/@aws-cdk/cloud-assembly-api/lib/index.ts b/packages/@aws-cdk/cloud-assembly-api/lib/index.ts index 835ab1034..1e3477534 100644 --- a/packages/@aws-cdk/cloud-assembly-api/lib/index.ts +++ b/packages/@aws-cdk/cloud-assembly-api/lib/index.ts @@ -19,8 +19,6 @@ export * from './metadata'; export * from './placeholders'; export * from './environment'; export * from './bootstrap'; -export * from './construct-tree'; -export * from './template-ranges'; export * from './stack-trace'; export * from './validation/formatting'; export * from './validation/parsing'; diff --git a/packages/@aws-cdk/cloud-assembly-api/lib/template-ranges.ts b/packages/@aws-cdk/cloud-assembly-api/lib/template-ranges.ts deleted file mode 100644 index 730c9c162..000000000 --- a/packages/@aws-cdk/cloud-assembly-api/lib/template-ranges.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { parse, type Pointers } from 'json-source-map'; - -/** - * A character-offset range into a template's text, as a half-open interval - * `[start, end)`. Framework-neutral on purpose: consumers map offsets to their - * own position model (for example the LSP via `TextDocument.positionAt`), which - * keeps this package free of editor types and of the UTF-16 column subtlety. - */ -export interface OffsetRange { - /** Start offset, a 0-based character index, inclusive. */ - readonly start: number; - /** End offset, a 0-based character index, exclusive. */ - readonly end: number; -} - -/** - * Resolves the character range of a resource's value block inside a synthesized - * CloudFormation template. The range covers the value `{ ... }` under - * `Resources/`, so `JSON.parse(text.slice(start, end))` returns the - * resource object. - * - * Returns `undefined` when the text is not valid JSON, or when there is no such - * resource. - */ -export function resolveResourceRange(templateText: string, logicalId: string): OffsetRange | undefined { - return indexTemplateRanges(templateText)?.block(logicalId); -} - -/** A resource's block range plus the range of each of its top-level properties. */ -export interface ResourceRanges { - /** The resource's value block `{ ... }` (value-only, like {@link resolveResourceRange}). */ - readonly block: OffsetRange; - /** Each top-level property keyed by name, covering the whole `"Key": value` entry. */ - readonly properties: Record; -} - -/** - * Resolves, in a single parse, a resource's block range and the range of each - * of its top-level properties. Property ranges span `"Key": value` so a - * navigation lands on the named property. - * - * Returns `undefined` when the text is not valid JSON or there is no such resource. - */ -export function resolveResourceRanges(templateText: string, logicalId: string): ResourceRanges | undefined { - return indexTemplateRanges(templateText)?.resource(logicalId); -} - -/** - * A template parsed once into a queryable range index, so a caller resolving - * several resources (for example a hover over a multi-resource construct) pays - * the parse cost once instead of per resource. - */ -export interface TemplateRanges { - /** The resource's value-block range, or `undefined` if there is no such resource. */ - block(logicalId: string): OffsetRange | undefined; - /** The resource's block plus its top-level property ranges, or `undefined` if absent. */ - resource(logicalId: string): ResourceRanges | undefined; -} - -/** - * Parse a template once into a {@link TemplateRanges} index. Returns `undefined` - * when the text is not valid JSON. The single-resource {@link resolveResourceRange} - * and {@link resolveResourceRanges} are thin wrappers over this. - */ -export function indexTemplateRanges(templateText: string): TemplateRanges | undefined { - const pointers = parsePointers(templateText); - if (pointers === undefined) { - return undefined; - } - return { - block: (logicalId) => blockRange(pointers, escapePointerSegment(logicalId)), - resource: (logicalId) => { - const escaped = escapePointerSegment(logicalId); - const block = blockRange(pointers, escaped); - return block === undefined ? undefined : { block, properties: propertyRanges(pointers, escaped) }; - }, - }; -} - -/** The value-block range of `/Resources/`, or `undefined` if absent. */ -function blockRange(pointers: Pointers, escapedId: string): OffsetRange | undefined { - const mapping = pointers[`/Resources/${escapedId}`]; - return mapping === undefined ? undefined : { start: mapping.value.pos, end: mapping.valueEnd.pos }; -} - -/** Key+value ranges of each top-level property of `/Resources/`. */ -function propertyRanges(pointers: Pointers, escapedId: string): Record { - const properties: Record = {}; - for (const [pointer, mapping] of Object.entries(pointers)) { - // This resource's top-level properties only. json-source-map types the key - // position as optional (absent for array items); a /Properties/ pointer - // is always an object member, so this narrows the type, not an impossible case. - const match = /^\/Resources\/([^/]+)\/Properties\/([^/]+)$/.exec(pointer); - if (match === null || match[1] !== escapedId || mapping.key === undefined) { - continue; - } - properties[unescapePointerSegment(match[2])] = { start: mapping.key.pos, end: mapping.valueEnd.pos }; - } - return properties; -} - -/** - * The inverse of `resolveResourceRange`: given a character offset into a - * template's text, returns the logical id of the resource whose block contains - * that offset, for linking a position in the template back to its construct. - * - * Returns `undefined` when the text is not valid JSON, or when the offset is not - * inside any `Resources/` block (for example whitespace, the - * `Resources` key, or another top-level section). - */ -export function resolveLogicalIdAtOffset(templateText: string, offset: number): string | undefined { - const pointers = parsePointers(templateText); - if (pointers === undefined) { - return undefined; - } - for (const [pointer, mapping] of Object.entries(pointers)) { - // Match only top-level resources (`/Resources/`), not nested property - // pointers like `/Resources//Properties/...`. - const match = /^\/Resources\/([^/]+)$/.exec(pointer); - if (match && offset >= mapping.value.pos && offset < mapping.valueEnd.pos) { - return unescapePointerSegment(match[1]); - } - } - return undefined; -} - -/** Parse the template into its JSON-pointer map, or `undefined` if it is not valid JSON. */ -function parsePointers(templateText: string): Pointers | undefined { - try { - return parse(templateText).pointers; - } catch { - return undefined; - } -} - -/** Escape a single path segment for use in a JSON pointer. */ -function escapePointerSegment(segment: string): string { - return segment.replace(/~/g, '~0').replace(/\//g, '~1'); -} - -/** Reverse of `escapePointerSegment`. */ -function unescapePointerSegment(segment: string): string { - return segment.replace(/~1/g, '/').replace(/~0/g, '~'); -} diff --git a/packages/@aws-cdk/cloud-assembly-api/package.json b/packages/@aws-cdk/cloud-assembly-api/package.json index 06bda2d20..ce5dc98f0 100644 --- a/packages/@aws-cdk/cloud-assembly-api/package.json +++ b/packages/@aws-cdk/cloud-assembly-api/package.json @@ -37,7 +37,6 @@ "@cdklabs/eslint-plugin": "^2.0.25", "@stylistic/eslint-plugin": "^3", "@types/jest": "^29.5.14", - "@types/json-source-map": "^0.6.0", "@types/node": "^20", "@typescript-eslint/eslint-plugin": "^8", "@typescript-eslint/parser": "^8", @@ -63,7 +62,6 @@ "@aws-cdk/cloud-assembly-schema": "^0.0.0" }, "dependencies": { - "json-source-map": "^0.6.1", "jsonschema": "^1.5.0", "semver": "^7.8.5" }, diff --git a/packages/@aws-cdk/cloud-assembly-api/test/construct-tree.test.ts b/packages/@aws-cdk/cloud-assembly-api/test/construct-tree.test.ts deleted file mode 100644 index 0bbf2eb60..000000000 --- a/packages/@aws-cdk/cloud-assembly-api/test/construct-tree.test.ts +++ /dev/null @@ -1,316 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { buildConstructTree, buildConstructTreeAsync, CloudAssembly, ConstructIndex, type ConstructTreeNode } from '../lib'; -import { rimraf } from './util'; - -const node = (nodePath: string, children: ConstructTreeNode[] = []): ConstructTreeNode => ({ - path: nodePath, - id: nodePath.split('/').pop() ?? nodePath, - children, -}); - -describe('ConstructIndex', () => { - test('empty tree has size 0 and no nodes', () => { - const index = ConstructIndex.fromTree([]); - expect(index.size).toBe(0); - expect([...index]).toEqual([]); - }); - - test('byPath looks up nodes in a flat tree', () => { - const index = ConstructIndex.fromTree([node('Stack1'), node('Stack2')]); - expect(index.byPath('Stack1')!.path).toBe('Stack1'); - expect(index.byPath('Stack2')!.path).toBe('Stack2'); - expect(index.byPath('Missing')).toBeUndefined(); - }); - - test('byPath indexes deep descendants', () => { - const tree = [ - node('Stack1', [ - node('Stack1/MyBucket', [ - node('Stack1/MyBucket/Resource'), - ]), - ]), - ]; - const index = ConstructIndex.fromTree(tree); - expect(index.byPath('Stack1/MyBucket/Resource')).toBeDefined(); - expect(index.size).toBe(3); - }); - - test('iterates every node in pre-order', () => { - const tree = [ - node('Stack1', [ - node('Stack1/A'), - node('Stack1/B', [node('Stack1/B/Resource')]), - ]), - ]; - const paths = [...ConstructIndex.fromTree(tree)].map((n) => n.path); - expect(paths).toEqual(['Stack1', 'Stack1/A', 'Stack1/B', 'Stack1/B/Resource']); - }); - - test('preserves the concrete node type through byPath and iteration', () => { - interface Rich extends ConstructTreeNode { - readonly children: readonly Rich[]; - readonly label: string; - } - const rich = (richPath: string, label: string): Rich => ({ path: richPath, id: richPath, label, children: [] }); - const index = ConstructIndex.fromTree([rich('A', 'alpha')]); - expect(index.byPath('A')?.label).toBe('alpha'); - expect([...index][0].label).toBe('alpha'); - }); -}); - -describe('buildConstructTree', () => { - let dir: string; - afterEach(() => dir && rimraf(dir)); - - // Deliberately non-default so the tests prove we read the filename from the - // manifest's tree artifact rather than assuming "tree.json". - const TREE_FILE = 'foo.tree.json'; - - function writeAssembly(opts: { withTree: boolean }): string { - dir = fs.mkdtempSync(path.join(os.tmpdir(), 'caa-tree-')); - const artifacts: Record = { - MyStack: { - type: 'aws:cloudformation:stack', - environment: 'aws://111/us-east-1', - properties: { templateFile: 'template.json' }, - metadata: { - '/MyStack/Bucket/Resource': [{ type: 'aws:cdk:logicalId', data: 'BucketABC' }], - }, - }, - }; - if (opts.withTree) { - artifacts.Tree = { type: 'cdk:tree', properties: { file: TREE_FILE } }; - } - fs.writeFileSync(path.join(dir, 'manifest.json'), JSON.stringify({ version: '0.0.0', artifacts })); - fs.writeFileSync(path.join(dir, 'template.json'), '{}'); - if (opts.withTree) { - const tree = { - version: 'tree-0.1', - tree: { - id: 'App', - path: '', - children: { - MyStack: { - id: 'MyStack', - path: 'MyStack', - children: { - Bucket: { - id: 'Bucket', - path: 'MyStack/Bucket', - children: { - Resource: { - id: 'Resource', - path: 'MyStack/Bucket/Resource', - attributes: { 'aws:cdk:cloudformation:type': 'AWS::S3::Bucket' }, - }, - }, - }, - // Internal node that must be filtered out. - CDKMetadata: { id: 'CDKMetadata', path: 'MyStack/CDKMetadata' }, - }, - }, - }, - }, - }; - fs.writeFileSync(path.join(dir, TREE_FILE), JSON.stringify(tree)); - } - return dir; - } - - test('reads the tree filename from the manifest and joins logicalId + CFN type', () => { - const assembly = new CloudAssembly(writeAssembly({ withTree: true })); - const tree = buildConstructTree(assembly, (fields) => fields); - - const resource = ConstructIndex.fromTree(tree).byPath('MyStack/Bucket/Resource'); - expect(resource?.type).toBe('AWS::S3::Bucket'); - expect(resource?.logicalId).toBe('BucketABC'); - }); - - test('filters cdk-internal nodes (e.g. CDKMetadata)', () => { - const assembly = new CloudAssembly(writeAssembly({ withTree: true })); - const paths = [...ConstructIndex.fromTree(buildConstructTree(assembly, (f) => f))].map((n) => n.path); - expect(paths).toContain('MyStack/Bucket'); - expect(paths).not.toContain('MyStack/CDKMetadata'); - }); - - test('passes the owning stack and construct path to the decorate callback', () => { - const assembly = new CloudAssembly(writeAssembly({ withTree: true })); - const tree = buildConstructTree(assembly, (fields, stack, constructPath) => ({ - ...fields, - stackId: stack?.id, - decoratedPath: constructPath, - })); - const resource = ConstructIndex.fromTree(tree).byPath('MyStack/Bucket/Resource'); - expect((resource as any)?.decoratedPath).toBe('MyStack/Bucket/Resource'); - expect((resource as any)?.stackId).toBe('MyStack'); - }); - - test('returns an empty tree when the assembly has no tree artifact', () => { - const assembly = new CloudAssembly(writeAssembly({ withTree: false })); - expect(buildConstructTree(assembly, (f) => f)).toEqual([]); - }); - - test('buildConstructTreeAsync awaits the decorator and produces the same joined tree', async () => { - const assembly = new CloudAssembly(writeAssembly({ withTree: true })); - const tree = await buildConstructTreeAsync(assembly, async (fields, stack, constructPath) => ({ - ...fields, - stackId: stack?.id, - // Resolve through a real Promise to prove the async decorator is awaited, - // not merely returned synchronously. - decoratedPath: await Promise.resolve(constructPath), - })); - const resource = ConstructIndex.fromTree(tree).byPath('MyStack/Bucket/Resource'); - expect(resource?.type).toBe('AWS::S3::Bucket'); - expect(resource?.logicalId).toBe('BucketABC'); - expect((resource as any)?.decoratedPath).toBe('MyStack/Bucket/Resource'); - expect((resource as any)?.stackId).toBe('MyStack'); - }); -}); - -describe('buildConstructTree -- nested stacks', () => { - let dir: string; - afterEach(() => dir && rimraf(dir)); - - const TREE_FILE = 'tree.json'; - - interface NestedOpts { - /** Logical ID of the bucket inside the nested stack (parent bucket is always 'ParentBucket'). */ - readonly nestedBucketLogicalId?: string; - /** false simulates --no-asset-metadata: the CfnStack carries no aws:asset:path. */ - readonly withAssetMetadata?: boolean; - /** false simulates a missing/unstaged asset: the nested template file isn't written. */ - readonly writeNestedTemplate?: boolean; - } - - /** - * Emits the real aws-cdk-lib NestedStack topology: a `Nested` construct, its - * sibling `Nested.NestedStack/Nested.NestedStackResource` AWS::CloudFormation::Stack - * (carrying aws:asset:path), and a bucket in both the parent and nested stacks. - * The `Nested` node deliberately carries a jsii fqn that does NOT end in - * ".NestedStack" -- so every test here also proves detection is by the sibling, - * not the fqn (P2b). - */ - function writeNestedAssembly(opts: NestedOpts = {}): string { - const withAsset = opts.withAssetMetadata ?? true; - const writeNested = opts.writeNestedTemplate ?? true; - const nestedBucketLid = opts.nestedBucketLogicalId ?? 'NestedBucket'; - - dir = fs.mkdtempSync(path.join(os.tmpdir(), 'caa-nested-')); - - const nestedStackResource: Record = { Type: 'AWS::CloudFormation::Stack' }; - if (withAsset) nestedStackResource.Metadata = { 'aws:asset:path': 'nested.template.json' }; - const parentTemplate = { Resources: { ParentBucket: { Type: 'AWS::S3::Bucket' }, NestedStackRes: nestedStackResource } }; - const nestedTemplate = { Resources: { [nestedBucketLid]: { Type: 'AWS::S3::Bucket' } } }; - - const artifacts = { - MyStack: { - type: 'aws:cloudformation:stack', - environment: 'aws://111/us-east-1', - properties: { templateFile: 'template.json' }, - metadata: { - '/MyStack/Bucket/Resource': [{ type: 'aws:cdk:logicalId', data: 'ParentBucket' }], - '/MyStack/Nested/Bucket/Resource': [{ type: 'aws:cdk:logicalId', data: nestedBucketLid }], - '/MyStack/Nested.NestedStack/Nested.NestedStackResource': [{ type: 'aws:cdk:logicalId', data: 'NestedStackRes' }], - }, - }, - Tree: { type: 'cdk:tree', properties: { file: TREE_FILE } }, - }; - - const tree = { - version: 'tree-0.1', - tree: { - id: 'App', - path: '', - children: { - MyStack: { - id: 'MyStack', - path: 'MyStack', - children: { - 'Bucket': { - id: 'Bucket', - path: 'MyStack/Bucket', - children: { Resource: { id: 'Resource', path: 'MyStack/Bucket/Resource', attributes: { 'aws:cdk:cloudformation:type': 'AWS::S3::Bucket' } } }, - }, - 'Nested': { - id: 'Nested', - path: 'MyStack/Nested', - // jsii fqn intentionally NOT ending in ".NestedStack" (P2b regression). - constructInfo: { fqn: 'my-lib.DatabaseNestedStack', version: '1.0.0' }, - children: { - Bucket: { - id: 'Bucket', - path: 'MyStack/Nested/Bucket', - children: { Resource: { id: 'Resource', path: 'MyStack/Nested/Bucket/Resource', attributes: { 'aws:cdk:cloudformation:type': 'AWS::S3::Bucket' } } }, - }, - }, - }, - 'Nested.NestedStack': { - id: 'Nested.NestedStack', - path: 'MyStack/Nested.NestedStack', - children: { - 'Nested.NestedStackResource': { - id: 'Nested.NestedStackResource', - path: 'MyStack/Nested.NestedStack/Nested.NestedStackResource', - attributes: { 'aws:cdk:cloudformation:type': 'AWS::CloudFormation::Stack' }, - }, - }, - }, - }, - }, - }, - }, - }; - - fs.writeFileSync(path.join(dir, 'manifest.json'), JSON.stringify({ version: '0.0.0', artifacts })); - fs.writeFileSync(path.join(dir, 'template.json'), JSON.stringify(parentTemplate)); - if (writeNested) fs.writeFileSync(path.join(dir, 'nested.template.json'), JSON.stringify(nestedTemplate)); - fs.writeFileSync(path.join(dir, TREE_FILE), JSON.stringify(tree)); - return dir; - } - - const templateFileOf = (assemblyDir: string, nodePath: string): string | undefined => - ConstructIndex.fromTree(buildConstructTree(new CloudAssembly(assemblyDir), (f) => f)).byPath(nodePath)?.templateFile; - - test('resolves nested resources to the nested template and parent resources to the parent template', () => { - const d = writeNestedAssembly(); - expect(templateFileOf(d, 'MyStack/Bucket/Resource')).toBe(path.join(d, 'template.json')); - expect(templateFileOf(d, 'MyStack/Nested/Bucket/Resource')).toBe(path.join(d, 'nested.template.json')); - }); - - test('resolves parent and nested twins (same logical ID) to their own templates', () => { - // NestedStack resets the logical-ID namespace, so both buckets are "ParentBucket". - // Positional threading must still send each to its own template. - const d = writeNestedAssembly({ nestedBucketLogicalId: 'ParentBucket' }); - expect(templateFileOf(d, 'MyStack/Bucket/Resource')).toBe(path.join(d, 'template.json')); - expect(templateFileOf(d, 'MyStack/Nested/Bucket/Resource')).toBe(path.join(d, 'nested.template.json')); - }); - - test('detects the NestedStack by its sibling resource, not the construct fqn (jsii subclass)', () => { - // The fixture's Nested node fqn is "my-lib.DatabaseNestedStack" (does not end in - // ".NestedStack"); a suffix gate would miss it and mis-inherit the parent template. - const d = writeNestedAssembly(); - expect(templateFileOf(d, 'MyStack/Nested/Bucket/Resource')).toBe(path.join(d, 'nested.template.json')); - }); - - test('yields no templateFile when the nested template is missing/unreadable', () => { - const d = writeNestedAssembly({ writeNestedTemplate: false }); - expect(templateFileOf(d, 'MyStack/Nested/Bucket/Resource')).toBeUndefined(); - expect(templateFileOf(d, 'MyStack/Bucket/Resource')).toBe(path.join(d, 'template.json')); - }); - - test('yields no templateFile when asset metadata is absent (--no-asset-metadata)', () => { - const d = writeNestedAssembly({ withAssetMetadata: false }); - expect(templateFileOf(d, 'MyStack/Nested/Bucket/Resource')).toBeUndefined(); - }); - - test('buildConstructTreeAsync threads nested-stack scope positionally via for...await (twin logical IDs)', async () => { - // Twin logical IDs (NestedStack resets the namespace): the async for...await - // recursion must still route each bucket to its own template, like the sync walk. - const d = writeNestedAssembly({ nestedBucketLogicalId: 'ParentBucket' }); - const index = ConstructIndex.fromTree(await buildConstructTreeAsync(new CloudAssembly(d), async (f) => f)); - expect(index.byPath('MyStack/Bucket/Resource')?.templateFile).toBe(path.join(d, 'template.json')); - expect(index.byPath('MyStack/Nested/Bucket/Resource')?.templateFile).toBe(path.join(d, 'nested.template.json')); - }); -}); diff --git a/packages/@aws-cdk/cloud-assembly-api/test/template-ranges.test.ts b/packages/@aws-cdk/cloud-assembly-api/test/template-ranges.test.ts deleted file mode 100644 index f09bdfdda..000000000 --- a/packages/@aws-cdk/cloud-assembly-api/test/template-ranges.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { indexTemplateRanges, resolveLogicalIdAtOffset, resolveResourceRange, resolveResourceRanges } from '../lib'; - -// A template object is the single source of truth: the text under test is its -// 1-space serialization (exactly how synth writes `*.template.json`), and the -// expected values are read straight off the object, so input and expectations -// can never drift. The shapes pack the cases that defeat naive approaches: -// -// - Policy carries an `Fn::Sub` (`${...}` braces), an `InlineJson` value with -// escaped quotes and a `}`, and a `Description` with a lone unmatched `{` — -// all of which break brace-counting but not a real parse. -// - MyBucket / MyBucketF68F3FF0 are a prefix-collision pair. -// - MyBucket also appears as a value (Fn::Sub, DependsOn), never as another key. -const TEMPLATE = { - Resources: { - MyBucket: { - Type: 'AWS::S3::Bucket', - Properties: { BucketName: 'plain-bucket' }, - }, - MyBucketF68F3FF0: { - Type: 'AWS::S3::Bucket', - Properties: { VersioningConfiguration: { Status: 'Enabled' } }, - }, - Policy: { - Type: 'AWS::IAM::Policy', - Properties: { - PolicyDocument: { - Statement: [{ Action: 's3:*', Resource: { 'Fn::Sub': 'arn:${AWS::Partition}:s3:::${MyBucket}/*' } }], - }, - InlineJson: '{"key":"va}lue"}', - Description: 'trailing brace } then a lone open { inside a string', - }, - DependsOn: ['MyBucket'], - }, - Topic: { Type: 'AWS::SNS::Topic' }, - }, -} as const; - -// 1-space indent mirrors `JSON.stringify(template, undefined, 1)` in synth. -const TEMPLATE_TEXT = JSON.stringify(TEMPLATE, undefined, 1); -const RESOURCES: Record = TEMPLATE.Resources; -const LOGICAL_IDS = Object.keys(RESOURCES); - -/** Slice the serialized text by a computed range and re-parse it. */ -const sliceParse = (range: { start: number; end: number }) => - JSON.parse(TEMPLATE_TEXT.slice(range.start, range.end)); - -describe('resolveResourceRange', () => { - // The authoritative check: a range is correct iff the substring it selects - // re-parses to the same value the template's own JSON.parse produced. No - // hand-computed offsets, and it runs over every resource. - test.each(LOGICAL_IDS)('the resolved range for %s re-parses to that exact resource', (logicalId) => { - const block = resolveResourceRange(TEMPLATE_TEXT, logicalId)!; - expect(sliceParse(block)).toEqual(RESOURCES[logicalId]); - }); - - test('resolves a block whose property values contain brace/quote hazards', () => { - // Policy holds an Fn::Sub (${...}), an InlineJson value with escaped quotes - // and a }, and a Description with a lone {, all of which defeat brace counting. - const block = resolveResourceRange(TEMPLATE_TEXT, 'Policy')!; - expect(sliceParse(block)).toEqual(RESOURCES.Policy); - }); - - test('a logical id resolves to its own block despite a prefix-named sibling', () => { - const short = resolveResourceRange(TEMPLATE_TEXT, 'MyBucket')!; - const long = resolveResourceRange(TEMPLATE_TEXT, 'MyBucketF68F3FF0')!; - expect(sliceParse(short)).toEqual(RESOURCES.MyBucket); - expect(sliceParse(long)).toEqual(RESOURCES.MyBucketF68F3FF0); - expect(short).not.toEqual(long); - }); - - test('resolves the definition block even when the id also appears as a value', () => { - // MyBucket appears under Policy's Fn::Sub and DependsOn, never as another key. - const block = resolveResourceRange(TEMPLATE_TEXT, 'MyBucket')!; - expect((sliceParse(block) as { Type: string }).Type).toBe('AWS::S3::Bucket'); - }); - - test('returns undefined for invalid JSON (for example a trailing comma)', () => { - // json-source-map is a strict parser, so a malformed template yields no - // range rather than a wrong one. Synthesized templates are always strict JSON. - const invalid = '{\n "Resources": {\n "B": {\n "Type": "AWS::S3::Bucket",\n }\n }\n}'; - expect(resolveResourceRange(invalid, 'B')).toBeUndefined(); - }); - - test('returns undefined for an unknown logical id', () => { - expect(resolveResourceRange(TEMPLATE_TEXT, 'DoesNotExist')).toBeUndefined(); - }); - - test('returns undefined when the text cannot be parsed into a tree', () => { - expect(resolveResourceRange('not json at all', 'MyBucket')).toBeUndefined(); - }); -}); - -describe('resolveLogicalIdAtOffset', () => { - // Inverse round-trip: an offset inside each resource's block resolves back to it. - test.each(LOGICAL_IDS)('an offset inside %s resolves to that logical id', (logicalId) => { - const block = resolveResourceRange(TEMPLATE_TEXT, logicalId)!; - const mid = Math.floor((block.start + block.end) / 2); - expect(resolveLogicalIdAtOffset(TEMPLATE_TEXT, mid)).toBe(logicalId); - }); - - test('an offset in a nested property value resolves to its owning resource', () => { - expect(resolveLogicalIdAtOffset(TEMPLATE_TEXT, TEMPLATE_TEXT.indexOf('lone open'))).toBe('Policy'); - }); - - test('returns undefined for an offset outside any resource block', () => { - // Offset 0 is the opening brace of the whole document, before Resources. - expect(resolveLogicalIdAtOffset(TEMPLATE_TEXT, 0)).toBeUndefined(); - }); - - test('returns undefined when the text cannot be parsed into a tree', () => { - expect(resolveLogicalIdAtOffset('not json at all', 3)).toBeUndefined(); - }); -}); - -describe('resolveResourceRanges', () => { - // Wrapping a key+value slice in braces makes it a valid object, so the same - // re-parse oracle proves each property range covers exactly `"Key": value`. - const sliceParseEntry = (range: { start: number; end: number }) => - JSON.parse('{' + TEMPLATE_TEXT.slice(range.start, range.end) + '}'); - - test.each(LOGICAL_IDS)('the block range for %s re-parses to that exact resource', (logicalId) => { - const { block } = resolveResourceRanges(TEMPLATE_TEXT, logicalId)!; - expect(sliceParse(block)).toEqual(RESOURCES[logicalId]); - }); - - test('each Policy property range re-parses to its exact `"Key": value` entry', () => { - // Policy's values include an Fn::Sub, an InlineJson string with braces and - // escaped quotes, and a Description with a lone brace -- hazards for anything - // but a real parse. - const { properties } = resolveResourceRanges(TEMPLATE_TEXT, 'Policy')!; - const expected = (RESOURCES.Policy as { Properties: Record }).Properties; - expect(Object.keys(properties).sort()).toEqual(Object.keys(expected).sort()); - for (const [name, range] of Object.entries(properties)) { - expect(sliceParseEntry(range)).toEqual({ [name]: expected[name] }); - } - }); - - test('property ranges include the key, not just the value', () => { - const { properties } = resolveResourceRanges(TEMPLATE_TEXT, 'MyBucket')!; - const slice = TEMPLATE_TEXT.slice(properties.BucketName.start, properties.BucketName.end); - expect(slice.startsWith('"BucketName"')).toBe(true); - expect(sliceParseEntry(properties.BucketName)).toEqual({ BucketName: 'plain-bucket' }); - }); - - test('enumerates only top-level properties, not nested keys', () => { - // PolicyDocument is one entry; its nested Statement/Action are not separate. - const { properties } = resolveResourceRanges(TEMPLATE_TEXT, 'Policy')!; - expect(Object.keys(properties).sort()).toEqual(['Description', 'InlineJson', 'PolicyDocument']); - }); - - test('a resource with no Properties yields an empty property map', () => { - const ranges = resolveResourceRanges(TEMPLATE_TEXT, 'Topic')!; - expect(ranges.properties).toEqual({}); - expect(sliceParse(ranges.block)).toEqual(RESOURCES.Topic); - }); - - test('returns undefined for an unknown logical id', () => { - expect(resolveResourceRanges(TEMPLATE_TEXT, 'DoesNotExist')).toBeUndefined(); - }); - - test('returns undefined when the text cannot be parsed into a tree', () => { - expect(resolveResourceRanges('not json at all', 'MyBucket')).toBeUndefined(); - }); -}); diff --git a/packages/@aws-cdk/cloud-assembly-api/tsconfig.json b/packages/@aws-cdk/cloud-assembly-api/tsconfig.json index 2581f15f1..dfdb6cc97 100644 --- a/packages/@aws-cdk/cloud-assembly-api/tsconfig.json +++ b/packages/@aws-cdk/cloud-assembly-api/tsconfig.json @@ -28,7 +28,6 @@ "target": "es2022", "types": [ "jest", - "json-source-map", "node" ], "incremental": true, diff --git a/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES b/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES index 0a240597c..eeca7d1f3 100644 --- a/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES +++ b/packages/@aws-cdk/integ-runner/THIRD_PARTY_LICENSES @@ -11149,7 +11149,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------- -** ip-address@10.7.4 - https://www.npmjs.com/package/ip-address/v/10.7.4 | MIT +** ip-address@10.7.5 - https://www.npmjs.com/package/ip-address/v/10.7.5 | MIT Copyright (C) 2011 by Beau Gunderson Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/packages/aws-cdk/THIRD_PARTY_LICENSES b/packages/aws-cdk/THIRD_PARTY_LICENSES index 34e0b3e09..1c12e50bc 100644 --- a/packages/aws-cdk/THIRD_PARTY_LICENSES +++ b/packages/aws-cdk/THIRD_PARTY_LICENSES @@ -11604,7 +11604,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------- -** ip-address@10.7.4 - https://www.npmjs.com/package/ip-address/v/10.7.4 | MIT +** ip-address@10.7.5 - https://www.npmjs.com/package/ip-address/v/10.7.5 | MIT Copyright (C) 2011 by Beau Gunderson Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/packages/cdk-assets/THIRD_PARTY_LICENSES b/packages/cdk-assets/THIRD_PARTY_LICENSES index e0dbda634..2c1f255a1 100644 --- a/packages/cdk-assets/THIRD_PARTY_LICENSES +++ b/packages/cdk-assets/THIRD_PARTY_LICENSES @@ -6058,32 +6058,6 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ----------------- - -** json-source-map@0.6.1 - https://www.npmjs.com/package/json-source-map/v/0.6.1 | MIT -MIT License - -Copyright (c) 2017 Evgeny Poberezkin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - ---------------- ** jsonschema@1.5.0 - https://www.npmjs.com/package/jsonschema/v/1.5.0 | MIT diff --git a/yarn.lock b/yarn.lock index 0d7fb74f8..a5ba25192 100644 --- a/yarn.lock +++ b/yarn.lock @@ -256,7 +256,6 @@ __metadata: "@cdklabs/eslint-plugin": "npm:^2.0.25" "@stylistic/eslint-plugin": "npm:^3" "@types/jest": "npm:^29.5.14" - "@types/json-source-map": "npm:^0.6.0" "@types/node": "npm:^20" "@typescript-eslint/eslint-plugin": "npm:^8" "@typescript-eslint/parser": "npm:^8" @@ -271,7 +270,6 @@ __metadata: eslint-plugin-prettier: "npm:^4.2.5" jest: "npm:^29.7.0" jest-junit: "npm:^16" - json-source-map: "npm:^0.6.1" jsonschema: "npm:^1.5.0" license-checker: "npm:^25.0.1" nx: "npm:^22.7.12" @@ -4430,13 +4428,6 @@ __metadata: languageName: node linkType: hard -"@types/json-source-map@npm:^0.6.0": - version: 0.6.0 - resolution: "@types/json-source-map@npm:0.6.0" - checksum: 10c0/b7db2f52735acc70b70bf49b1e945f4749ac5c9c8429c44ad23c751193f89d030400ed966c416540647745414cd450f35795395bf0be9eae94816bbe03ad0a2f - languageName: node - linkType: hard - "@types/json5@npm:^0.0.29": version: 0.0.29 resolution: "@types/json5@npm:0.0.29"