@simplysf/simply-utam 0.1.7 → 0.3.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.
Files changed (34) hide show
  1. package/README.md +124 -1
  2. package/lib/clipboard.d.ts +31 -0
  3. package/lib/clipboard.js +98 -0
  4. package/lib/clipboard.js.map +1 -0
  5. package/lib/commands/init.d.ts +2 -1
  6. package/lib/commands/init.js +39 -13
  7. package/lib/commands/init.js.map +1 -1
  8. package/lib/commands/snapshot/console/import.d.ts +26 -0
  9. package/lib/commands/snapshot/console/import.js +96 -0
  10. package/lib/commands/snapshot/console/import.js.map +1 -0
  11. package/lib/commands/snapshot/console/snippet.d.ts +25 -0
  12. package/lib/commands/snapshot/console/snippet.js +72 -0
  13. package/lib/commands/snapshot/console/snippet.js.map +1 -0
  14. package/lib/commands/snapshot/find.d.ts +25 -0
  15. package/lib/commands/snapshot/find.js +82 -0
  16. package/lib/commands/snapshot/find.js.map +1 -0
  17. package/lib/commands/snapshot/lookup.d.ts +20 -0
  18. package/lib/commands/snapshot/lookup.js +57 -0
  19. package/lib/commands/snapshot/lookup.js.map +1 -0
  20. package/lib/commands/snapshot/node.d.ts +22 -0
  21. package/lib/commands/snapshot/node.js +61 -0
  22. package/lib/commands/snapshot/node.js.map +1 -0
  23. package/lib/commands/snapshot/path.d.ts +21 -0
  24. package/lib/commands/snapshot/path.js +77 -0
  25. package/lib/commands/snapshot/path.js.map +1 -0
  26. package/lib/commands/snapshot/tree.d.ts +19 -0
  27. package/lib/commands/snapshot/tree.js +50 -0
  28. package/lib/commands/snapshot/tree.js.map +1 -0
  29. package/lib/snapshot-flags.d.ts +40 -0
  30. package/lib/snapshot-flags.js +70 -0
  31. package/lib/snapshot-flags.js.map +1 -0
  32. package/oclif.manifest.json +553 -3
  33. package/package.json +16 -4
  34. package/skills/utam-dom-snapshots/SKILL.md +390 -0
@@ -0,0 +1,72 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Command, Flags } from '@oclif/core';
17
+ import { domSnapshotSnippet } from '@simplysf/simply-utam-core';
18
+ import { ClipboardUnavailableError, systemClipboard } from '../../../clipboard.js';
19
+ import { BAD_INPUT } from '../../../snapshot-flags.js';
20
+ /**
21
+ * Print the console snippet that captures a DOM snapshot, or copy it to the clipboard.
22
+ */
23
+ export default class SnapshotConsoleSnippet extends Command {
24
+ static summary = "The snippet to paste into a browser's DevTools console to capture a DOM snapshot.";
25
+ static description = "Paste it into the console of the page to capture, with the console's context set to top. It runs the same walker a test run does, masks the page there, copies the snapshot JSON to the clipboard and prints a summary; save it with `snapshot console import`. Where the console's copy() isn't available, it downloads the JSON instead, for `import --file`. The mask is fixed in the snippet.";
26
+ static examples = [
27
+ '<%= config.bin %> <%= command.id %> --copy',
28
+ '<%= config.bin %> <%= command.id %> --mask structure --copy',
29
+ '<%= config.bin %> <%= command.id %> > snippet.js',
30
+ ];
31
+ static enableJsonFlag = true;
32
+ static flags = {
33
+ copy: Flags.boolean({ summary: 'Copy the snippet to the clipboard instead of printing it.', default: false }),
34
+ mask: Flags.string({
35
+ summary: 'Mask mode, from least kept to most. Defaults to UTAM_SNAPSHOT_MASK, then labels.',
36
+ options: ['structure', 'labels', 'aria', 'full'],
37
+ }),
38
+ focus: Flags.string({
39
+ summary: 'Where the walk starts: auto (the active Lightning page or console tab), none, or a CSS selector.',
40
+ default: 'auto',
41
+ }),
42
+ 'max-nodes': Flags.integer({ summary: 'Most visible nodes to capture.', min: 1 }),
43
+ 'max-hidden-nodes': Flags.integer({ summary: 'Most hidden nodes to capture.', min: 1 }),
44
+ };
45
+ /** The clipboard `--copy` writes to; tests replace it. */
46
+ clipboard = systemClipboard();
47
+ async run() {
48
+ const { flags } = await this.parse(SnapshotConsoleSnippet);
49
+ const snippet = domSnapshotSnippet({
50
+ mask: flags.mask,
51
+ focus: flags.focus,
52
+ maxNodes: flags['max-nodes'],
53
+ maxHiddenNodes: flags['max-hidden-nodes'],
54
+ log: (message) => this.warn(message),
55
+ });
56
+ if (!flags.copy) {
57
+ this.log(snippet);
58
+ return { snippet, copied: false };
59
+ }
60
+ try {
61
+ await this.clipboard.write(snippet);
62
+ }
63
+ catch (error) {
64
+ if (!(error instanceof ClipboardUnavailableError))
65
+ throw error;
66
+ this.error(`Can't copy the snippet: ${error.message}. Run without --copy to print it.`, { exit: BAD_INPUT });
67
+ }
68
+ this.log('Snippet copied. Paste it into the DevTools console of the page to capture (context: top), then run `npx simply-utam snapshot console import`.');
69
+ return { snippet, copied: true };
70
+ }
71
+ }
72
+ //# sourceMappingURL=snippet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippet.js","sourceRoot":"","sources":["../../../../src/commands/snapshot/console/snippet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAqB,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAkB,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAIvD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,sBAAuB,SAAQ,OAAO;IAClD,MAAM,CAAU,OAAO,GAAG,mFAAmF,CAAC;IAE9G,MAAM,CAAU,WAAW,GAChC,mYAAmY,CAAC;IAE/X,MAAM,CAAU,QAAQ,GAAG;QAChC,4CAA4C;QAC5C,6DAA6D;QAC7D,kDAAkD;KACnD,CAAC;IAEK,MAAM,CAAU,cAAc,GAAG,IAAI,CAAC;IAEtC,MAAM,CAAU,KAAK,GAAG;QAC7B,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,2DAA2D,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC7G,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC;YACjB,OAAO,EAAE,kFAAkF;YAC3F,OAAO,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SACjD,CAAC;QACF,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC;YAClB,OAAO,EAAE,kGAAkG;YAC3G,OAAO,EAAE,MAAM;SAChB,CAAC;QACF,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,gCAAgC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;QACjF,kBAAkB,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,+BAA+B,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;KACxF,CAAC;IAEF,0DAA0D;IACnD,SAAS,GAAc,eAAe,EAAE,CAAC;IAEzC,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,kBAAkB,CAAC;YACjC,IAAI,EAAE,KAAK,CAAC,IAAgC;YAC5C,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC;YAC5B,cAAc,EAAE,KAAK,CAAC,kBAAkB,CAAC;YACzC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;SACrC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClB,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACpC,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,yBAAyB,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC/D,IAAI,CAAC,KAAK,CAAC,2BAA2B,KAAK,CAAC,OAAO,mCAAmC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/G,CAAC;QACD,IAAI,CAAC,GAAG,CACN,+IAA+I,CAChJ,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type FindResult } from '@simplysf/simply-utam-core';
3
+ /**
4
+ * Find nodes in a DOM snapshot by text, attribute or tag.
5
+ */
6
+ export default class SnapshotFind extends Command {
7
+ static summary: string;
8
+ static description: string;
9
+ static examples: string[];
10
+ static enableJsonFlag: boolean;
11
+ static flags: {
12
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ text: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ attr: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ tag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ class: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ css: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ within: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ shadow: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
+ };
24
+ run(): Promise<FindResult>;
25
+ }
@@ -0,0 +1,82 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Command, Flags } from '@oclif/core';
17
+ import { DomSnapshotError, findNodes } from '@simplysf/simply-utam-core';
18
+ import { BAD_INPUT, fileFlag, limitFlag, loadSnapshot, projectDirFlag } from '../../snapshot-flags.js';
19
+ /**
20
+ * Find nodes in a DOM snapshot by text, attribute or tag.
21
+ */
22
+ export default class SnapshotFind extends Command {
23
+ static summary = 'Find nodes in a DOM snapshot by text, attribute, class, tag or CSS selector.';
24
+ static description = "Text matching is case-insensitive and substring, over each node's own text, its shadow text and its attribute values (not class). Record values are masked, so search by labels. Visible matches are listed first, in document order; hidden matches are counted, or listed with --all. --attr name=value is a case-insensitive substring match; --class matches one whole class token. --css tests a selector as UTAM's findElement would: every match in document order, the first (the one UTAM takes) marked, hidden ones included. It searches the document, or with --within a node's light DOM (a page object's elements) or, adding --shadow, its shadow root (shadow elements).";
25
+ static examples = [
26
+ '<%= config.bin %> <%= command.id %> --text "Account Name"',
27
+ '<%= config.bin %> <%= command.id %> --tag records-record-layout-item --all',
28
+ '<%= config.bin %> <%= command.id %> --attr title=Edit',
29
+ '<%= config.bin %> <%= command.id %> --css "lightning-button.slds-button_brand"',
30
+ '<%= config.bin %> <%= command.id %> --css "lightning-input" --within n5012 --shadow',
31
+ ];
32
+ static enableJsonFlag = true;
33
+ static flags = {
34
+ text: Flags.string({ summary: 'Text to match (case-insensitive substring).' }),
35
+ attr: Flags.string({ summary: 'Attribute to match: `name` (present) or `name=value` (substring).' }),
36
+ tag: Flags.string({ summary: 'Tag name to match exactly.' }),
37
+ class: Flags.string({ summary: 'A whole class token to match, as `.name` would (case-sensitive).' }),
38
+ css: Flags.string({
39
+ summary: 'A CSS selector to test as UTAM would: all matches in document order, the first marked.',
40
+ exclusive: ['text', 'attr', 'tag', 'class'],
41
+ }),
42
+ within: Flags.string({ summary: 'With --css: search under this node (its light DOM).', dependsOn: ['css'] }),
43
+ shadow: Flags.boolean({
44
+ summary: "With --css and --within: search the node's shadow root instead.",
45
+ // No default: oclif checks dependsOn against a default value too.
46
+ dependsOn: ['css', 'within'],
47
+ }),
48
+ all: Flags.boolean({ summary: 'List hidden matches in full rather than as a count.', default: false }),
49
+ ...fileFlag,
50
+ ...projectDirFlag,
51
+ ...limitFlag,
52
+ };
53
+ async run() {
54
+ const { flags } = await this.parse(SnapshotFind);
55
+ if ([flags.text, flags.attr, flags.tag, flags.class, flags.css].every((value) => value === undefined)) {
56
+ this.error('Give --text, --attr, --class, --tag or --css.', { exit: BAD_INPUT });
57
+ }
58
+ try {
59
+ const { snapshot, header } = await loadSnapshot(flags['project-dir'], flags.file);
60
+ const result = findNodes(snapshot, {
61
+ text: flags.text,
62
+ attr: flags.attr,
63
+ tag: flags.tag,
64
+ class: flags.class,
65
+ css: flags.css,
66
+ within: flags.within,
67
+ shadow: flags.shadow,
68
+ all: flags.all,
69
+ limit: flags.limit,
70
+ });
71
+ for (const line of [...header, ...result.lines])
72
+ this.log(line);
73
+ return result.json;
74
+ }
75
+ catch (error) {
76
+ if (error instanceof DomSnapshotError)
77
+ this.error(error.message, { exit: BAD_INPUT });
78
+ throw error;
79
+ }
80
+ }
81
+ }
82
+ //# sourceMappingURL=find.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find.js","sourceRoot":"","sources":["../../../src/commands/snapshot/find.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type LookupResult, type SearchResult } from '@simplysf/simply-utam-core';
3
+ /**
4
+ * Look up the page objects for a tag, or search them by keyword. Doesn't need a snapshot.
5
+ */
6
+ export default class SnapshotLookup extends Command {
7
+ static summary: string;
8
+ static description: string;
9
+ static examples: string[];
10
+ static enableJsonFlag: boolean;
11
+ static args: {
12
+ tag: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
13
+ };
14
+ static flags: {
15
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
16
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ };
19
+ run(): Promise<LookupResult | SearchResult>;
20
+ }
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Args, Command, Flags } from '@oclif/core';
17
+ import { buildPageObjectIndex, lookupPageObject, searchPageObjects, } from '@simplysf/simply-utam-core';
18
+ import { BAD_INPUT, limitFlag, projectDirFlag } from '../../snapshot-flags.js';
19
+ /**
20
+ * Look up the page objects for a tag, or search them by keyword. Doesn't need a snapshot.
21
+ */
22
+ export default class SnapshotLookup extends Command {
23
+ static summary = 'Show the page objects for a tag, with their import paths, methods and elements; or search them.';
24
+ static description = "Reads the project's own page objects (from utam.config.json) and the page-object packages that generator.config.json namespaces and .utam/namespace-map.json map to, such as salesforce-pageobjects. A tag maps to a page object through a root selector, an element typed as that page object, or the naming convention. Where a tag maps to an implementation of an interface, the interface is reported, since test code loads the interface. Also accepts a page object name (such as recordHomeFlexipage2) instead of a tag. With --search, lists the page objects whose name, import path, tags or description contain every word given.";
25
+ static examples = [
26
+ '<%= config.bin %> <%= command.id %> lightning-input',
27
+ '<%= config.bin %> <%= command.id %> one-record-home-flexipage2',
28
+ '<%= config.bin %> <%= command.id %> recordLayoutItem',
29
+ '<%= config.bin %> <%= command.id %> --search "related list"',
30
+ ];
31
+ static enableJsonFlag = true;
32
+ static args = {
33
+ tag: Args.string({ description: 'A custom element tag, or a page object name.' }),
34
+ };
35
+ static flags = {
36
+ search: Flags.string({ summary: 'Words to search for in page object names, import paths, tags and descriptions.' }),
37
+ ...projectDirFlag,
38
+ ...limitFlag,
39
+ };
40
+ async run() {
41
+ const { args, flags } = await this.parse(SnapshotLookup);
42
+ if ((args.tag === undefined) === (flags.search === undefined)) {
43
+ this.error('Give a tag or page object name, or --search <words>, but not both.', { exit: BAD_INPUT });
44
+ }
45
+ const index = buildPageObjectIndex({ projectDir: flags['project-dir'] });
46
+ for (const warning of index.warnings)
47
+ this.warn(warning);
48
+ const options = { limit: flags.limit };
49
+ const result = flags.search === undefined
50
+ ? lookupPageObject(index, args.tag, options)
51
+ : searchPageObjects(index, flags.search, options);
52
+ for (const line of result.lines)
53
+ this.log(line);
54
+ return result.json;
55
+ }
56
+ }
57
+ //# sourceMappingURL=lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../../src/commands/snapshot/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAGlB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE/E;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,OAAO;IAC1C,MAAM,CAAU,OAAO,GAC5B,iGAAiG,CAAC;IAE7F,MAAM,CAAU,WAAW,GAChC,gnBAAgnB,CAAC;IAE5mB,MAAM,CAAU,QAAQ,GAAG;QAChC,qDAAqD;QACrD,gEAAgE;QAChE,sDAAsD;QACtD,6DAA6D;KAC9D,CAAC;IAEK,MAAM,CAAU,cAAc,GAAG,IAAI,CAAC;IAEtC,MAAM,CAAU,IAAI,GAAG;QAC5B,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;KAClF,CAAC;IAEK,MAAM,CAAU,KAAK,GAAG;QAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,gFAAgF,EAAE,CAAC;QACnH,GAAG,cAAc;QACjB,GAAG,SAAS;KACb,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,KAAK,CAAC,oEAAoE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACxG,CAAC;QACD,MAAM,KAAK,GAAG,oBAAoB,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACzE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACvC,MAAM,MAAM,GACV,KAAK,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,GAAI,EAAE,OAAO,CAAC;YAC7C,CAAC,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK;YAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type AncestorsResult, type NodeDetails } from '@simplysf/simply-utam-core';
3
+ /**
4
+ * Print one node in full.
5
+ */
6
+ export default class SnapshotNode extends Command {
7
+ static summary: string;
8
+ static description: string;
9
+ static examples: string[];
10
+ static enableJsonFlag: boolean;
11
+ static args: {
12
+ id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
13
+ };
14
+ static flags: {
15
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
16
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ node: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ ancestors: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<NodeDetails | AncestorsResult>;
22
+ }
@@ -0,0 +1,61 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Args, Command, Flags } from '@oclif/core';
17
+ import { DomSnapshotError, nodeAncestors, nodeDetails, } from '@simplysf/simply-utam-core';
18
+ import { BAD_INPUT, fileFlag, limitFlag, loadSnapshot, projectDirFlag } from '../../snapshot-flags.js';
19
+ /**
20
+ * Print one node in full.
21
+ */
22
+ export default class SnapshotNode extends Command {
23
+ static summary = 'Show everything a snapshot recorded about one node.';
24
+ static description = 'Its handle and its address (child positions from the page root, s for a step into a shadow root; the same in another snapshot of the same page). Every recorded attribute, unclipped; its own text and shadow text; its size and position; whether it is hidden, and under which node; its parent; and each of its light DOM and shadow root children in one line. With --ancestors, its ancestors instead, nearest first, one line each, with a line wherever the chain leaves a shadow root: for picking a container to scope a selector with.';
25
+ static examples = [
26
+ '<%= config.bin %> <%= command.id %> n5012',
27
+ '<%= config.bin %> <%= command.id %> --node n5012',
28
+ '<%= config.bin %> <%= command.id %> n5012 --ancestors',
29
+ ];
30
+ static enableJsonFlag = true;
31
+ static args = {
32
+ id: Args.string({ description: 'The node handle, such as n5012 (from find, path or tree).' }),
33
+ };
34
+ static flags = {
35
+ node: Flags.string({ summary: 'The node handle, instead of the argument.' }),
36
+ ancestors: Flags.boolean({ summary: "List the node's ancestors, nearest first, instead.", default: false }),
37
+ ...fileFlag,
38
+ ...projectDirFlag,
39
+ ...limitFlag,
40
+ };
41
+ async run() {
42
+ const { args, flags } = await this.parse(SnapshotNode);
43
+ const id = args.id ?? flags.node;
44
+ if (!id)
45
+ this.error('Give a node handle: snapshot node <n…>.', { exit: BAD_INPUT });
46
+ try {
47
+ const { snapshot, header } = await loadSnapshot(flags['project-dir'], flags.file);
48
+ const options = { limit: flags.limit };
49
+ const result = flags.ancestors ? nodeAncestors(snapshot, id, options) : nodeDetails(snapshot, id, options);
50
+ for (const line of [...header, ...result.lines])
51
+ this.log(line);
52
+ return result.json;
53
+ }
54
+ catch (error) {
55
+ if (error instanceof DomSnapshotError)
56
+ this.error(error.message, { exit: BAD_INPUT });
57
+ throw error;
58
+ }
59
+ }
60
+ }
61
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../../src/commands/snapshot/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,GAGZ,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEvG;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IACxC,MAAM,CAAU,OAAO,GAAG,qDAAqD,CAAC;IAEhF,MAAM,CAAU,WAAW,GAChC,khBAAkhB,CAAC;IAE9gB,MAAM,CAAU,QAAQ,GAAG;QAChC,2CAA2C;QAC3C,kDAAkD;QAClD,uDAAuD;KACxD,CAAC;IAEK,MAAM,CAAU,cAAc,GAAG,IAAI,CAAC;IAEtC,MAAM,CAAU,IAAI,GAAG;QAC5B,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC;KAC9F,CAAC;IAEK,MAAM,CAAU,KAAK,GAAG;QAC7B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;QAC5E,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,oDAAoD,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC3G,GAAG,QAAQ;QACX,GAAG,cAAc;QACjB,GAAG,SAAS;KACb,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC;QACjC,IAAI,CAAC,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,yCAAyC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAClF,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;YAC3G,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;gBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChE,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,gBAAgB;gBAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YACtF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type FindResult, type PathResult } from '@simplysf/simply-utam-core';
3
+ /**
4
+ * Show the component path to a node, with what the page-object index knows about each element.
5
+ */
6
+ export default class SnapshotPath extends Command {
7
+ static summary: string;
8
+ static description: string;
9
+ static examples: string[];
10
+ static enableJsonFlag: boolean;
11
+ static flags: {
12
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ node: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ text: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ };
18
+ run(): Promise<PathResult | {
19
+ candidates: FindResult;
20
+ }>;
21
+ }
@@ -0,0 +1,77 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Command, Flags } from '@oclif/core';
17
+ import { buildPageObjectIndex, componentPath, componentPathByText, DomSnapshotError, } from '@simplysf/simply-utam-core';
18
+ import { BAD_INPUT, fileFlag, limitFlag, loadSnapshot, projectDirFlag } from '../../snapshot-flags.js';
19
+ /**
20
+ * Show the component path to a node, with what the page-object index knows about each element.
21
+ */
22
+ export default class SnapshotPath extends Command {
23
+ static summary = 'Show the custom elements from the document down to a node, with their page objects.';
24
+ static description = 'Marks each shadow crossing and each hidden ancestor, and annotates each custom element with its page object (import path, and root when utam.load can load it), a likely route through a page object above it ("reachable through"), or "no page object found". With --text, prints the path only when exactly one visible node matches; otherwise lists the candidates and exits with code 1.';
25
+ static examples = [
26
+ '<%= config.bin %> <%= command.id %> --node n5012',
27
+ '<%= config.bin %> <%= command.id %> --text "Account Name"',
28
+ ];
29
+ static enableJsonFlag = true;
30
+ static flags = {
31
+ node: Flags.string({
32
+ summary: 'The target node handle, such as n5012 (from find or tree).',
33
+ exactlyOne: ['node', 'text'],
34
+ }),
35
+ text: Flags.string({
36
+ summary: 'Find the target by text; it must have exactly one visible match.',
37
+ exactlyOne: ['node', 'text'],
38
+ }),
39
+ ...fileFlag,
40
+ ...projectDirFlag,
41
+ ...limitFlag,
42
+ };
43
+ async run() {
44
+ const { flags } = await this.parse(SnapshotPath);
45
+ try {
46
+ // Built once, after the snapshot is read, so a missing snapshot doesn't index every package first.
47
+ let built;
48
+ const pageObjects = () => {
49
+ if (!built) {
50
+ built = buildPageObjectIndex({ projectDir: flags['project-dir'] });
51
+ for (const warning of built.warnings)
52
+ this.warn(warning);
53
+ }
54
+ return built;
55
+ };
56
+ const { snapshot, header } = await loadSnapshot(flags['project-dir'], flags.file, pageObjects);
57
+ const options = { index: pageObjects(), limit: flags.limit };
58
+ if (flags.node !== undefined) {
59
+ const result = componentPath(snapshot, flags.node, options);
60
+ for (const line of [...header, ...result.lines])
61
+ this.log(line);
62
+ return result.json;
63
+ }
64
+ const result = componentPathByText(snapshot, flags.text, options);
65
+ for (const line of [...header, ...result.lines])
66
+ this.log(line);
67
+ process.exitCode = result.exitCode;
68
+ return result.json;
69
+ }
70
+ catch (error) {
71
+ if (error instanceof DomSnapshotError)
72
+ this.error(error.message, { exit: BAD_INPUT });
73
+ throw error;
74
+ }
75
+ }
76
+ }
77
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../../src/commands/snapshot/path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type DomNode } from '@simplysf/simply-utam-core';
3
+ /**
4
+ * Print the pruned tree under a node.
5
+ */
6
+ export default class SnapshotTree extends Command {
7
+ static summary: string;
8
+ static description: string;
9
+ static examples: string[];
10
+ static enableJsonFlag: boolean;
11
+ static flags: {
12
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ node: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ depth: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
17
+ };
18
+ run(): Promise<DomNode>;
19
+ }
@@ -0,0 +1,50 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Command, Flags } from '@oclif/core';
17
+ import { DomSnapshotError, subtree } from '@simplysf/simply-utam-core';
18
+ import { BAD_INPUT, fileFlag, limitFlag, loadSnapshot, projectDirFlag } from '../../snapshot-flags.js';
19
+ /**
20
+ * Print the pruned tree under a node.
21
+ */
22
+ export default class SnapshotTree extends Command {
23
+ static summary = 'Print the pruned tree under a node, to a depth.';
24
+ static description = 'Each line is one node: tag, #id and .classes as CSS, other attributes, its own text and its shadow text, then its node handle (n…). "#shadow(mode)" lines mark a shadow root; "(hidden)" marks the top of a hidden subtree.';
25
+ static examples = ['<%= config.bin %> <%= command.id %> --node n5012 --depth 3'];
26
+ static enableJsonFlag = true;
27
+ static flags = {
28
+ node: Flags.string({ summary: 'The node to start at. Defaults to the snapshot root.' }),
29
+ depth: Flags.integer({ summary: 'How many levels below the node to print.', default: 3 }),
30
+ ...fileFlag,
31
+ ...projectDirFlag,
32
+ ...limitFlag,
33
+ };
34
+ async run() {
35
+ const { flags } = await this.parse(SnapshotTree);
36
+ try {
37
+ const { snapshot, header } = await loadSnapshot(flags['project-dir'], flags.file);
38
+ const result = subtree(snapshot, flags.node, { depth: flags.depth, limit: flags.limit });
39
+ for (const line of [...header, ...result.lines])
40
+ this.log(line);
41
+ return result.json;
42
+ }
43
+ catch (error) {
44
+ if (error instanceof DomSnapshotError)
45
+ this.error(error.message, { exit: BAD_INPUT });
46
+ throw error;
47
+ }
48
+ }
49
+ }
50
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../../../src/commands/snapshot/tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAgB,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEvG;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,OAAO;IACxC,MAAM,CAAU,OAAO,GAAG,iDAAiD,CAAC;IAE5E,MAAM,CAAU,WAAW,GAChC,6NAA6N,CAAC;IAEzN,MAAM,CAAU,QAAQ,GAAG,CAAC,4DAA4D,CAAC,CAAC;IAE1F,MAAM,CAAU,cAAc,GAAG,IAAI,CAAC;IAEtC,MAAM,CAAU,KAAK,GAAG;QAC7B,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;QACvF,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,0CAA0C,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACzF,GAAG,QAAQ;QACX,GAAG,cAAc;QACjB,GAAG,SAAS;KACb,CAAC;IAEK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAClF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACzF,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;gBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChE,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,gBAAgB;gBAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YACtF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { type DomSnapshot, type PageObjectIndex } from '@simplysf/simply-utam-core';
2
+ /** Flags the `snapshot` commands share. */
3
+ export declare const fileFlag: {
4
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
5
+ };
6
+ export declare const projectDirFlag: {
7
+ 'project-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ export declare const limitFlag: {
10
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ /** Exit code 2: bad input, such as a missing snapshot, an unknown handle, or conflicting arguments. */
13
+ export declare const BAD_INPUT = 2;
14
+ /** A snapshot a query command was pointed at, and its header. */
15
+ export type LoadedSnapshot = {
16
+ snapshot: DomSnapshot;
17
+ header: string[];
18
+ };
19
+ /** The page-object index, or a function that builds it when it's needed. */
20
+ export type IndexSource = PageObjectIndex | (() => PageObjectIndex);
21
+ /**
22
+ * The header for a snapshot read from, or saved to, `file`, naming the file relative to the current
23
+ * folder.
24
+ *
25
+ * @param projectDir The test project.
26
+ * @param file The snapshot's file, absolute or relative to the current folder.
27
+ * @param snapshot The snapshot.
28
+ * @param index The page-object index, when the command has one; otherwise it's built only for UTAM's
29
+ * not-found error.
30
+ */
31
+ export declare function headerFor(projectDir: string, file: string, snapshot: DomSnapshot, index?: IndexSource): string[];
32
+ /**
33
+ * Read the snapshot a query command was pointed at, and the header that names it.
34
+ *
35
+ * @param projectDir The test project.
36
+ * @param file The `--file` flag; the project's latest snapshot when omitted.
37
+ * @param index The page-object index, when the command has one; otherwise it's built only for UTAM's
38
+ * not-found error.
39
+ */
40
+ export declare function loadSnapshot(projectDir: string, file?: string, index?: IndexSource): Promise<LoadedSnapshot>;