@razorwind/nx 0.0.4 → 0.0.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  # Changelog for Razorwind - Nx
4
4
 
5
+ ## [0.0.6](https://github.com/storm-software/razorwind/releases/tag/nx%400.0.6) (07/30/2026)
6
+
7
+ ### Features
8
+
9
+ - **nx:** Added sync generator to Nx plugin ([b72ebe4](https://github.com/storm-software/razorwind/commit/b72ebe4))
10
+
11
+ ### Updated Dependencies
12
+
13
+ - Updated **core** to **v0.0.12**
14
+
15
+ ## [0.0.5](https://github.com/storm-software/razorwind/releases/tag/nx%400.0.5) (07/30/2026)
16
+
17
+ ### Features
18
+
19
+ - **core:** Update generator to read `componentsPath` to populate specification ([77ce658](https://github.com/storm-software/razorwind/commit/77ce658))
20
+
21
+ ### Updated Dependencies
22
+
23
+ - Updated **core** to **v0.0.11**
24
+
5
25
  ## [0.0.4](https://github.com/storm-software/razorwind/releases/tag/nx%400.0.4) (07/27/2026)
6
26
 
7
27
  ### Updated Dependencies
@@ -19,10 +19,10 @@ interface GenerateExecutorSchema {
19
19
  */
20
20
  mode: string;
21
21
  /**
22
- * The path to the registry.json file
22
+ * The path to a directory containing component directories or files
23
23
  *
24
24
  */
25
- registryPath?: string;
25
+ componentsPath?: string;
26
26
  /**
27
27
  * The path to the tokens.json file
28
28
  *
@@ -19,10 +19,10 @@ interface GenerateExecutorSchema {
19
19
  */
20
20
  mode: string;
21
21
  /**
22
- * The path to the registry.json file
22
+ * The path to a directory containing component directories or files
23
23
  *
24
24
  */
25
- registryPath?: string;
25
+ componentsPath?: string;
26
26
  /**
27
27
  * The path to the tokens.json file
28
28
  *
@@ -6,10 +6,8 @@ async function executorFn(options, context) {
6
6
  if (!context.projectName) throw new Error(`The Razorwind - Generate executor requires \`projectName\` on the context object.`);
7
7
  if (!context.projectName || !context.projectsConfigurations?.projects || !context.projectsConfigurations.projects[context.projectName] || !context.projectsConfigurations.projects[context.projectName]?.root) throw new Error(`The Razorwind - Generate executor requires \`projectsConfigurations\` on the context object.`);
8
8
  await (0, _power_plant_core.execute)(_razorwind_core.generator, {
9
- configFile: options.configFile,
10
- mode: options.mode,
11
- registryPath: options.registryPath,
12
- tokensPath: options.tokensPath
9
+ ...options,
10
+ mode: options.mode
13
11
  });
14
12
  return { success: true };
15
13
  }
@@ -6,10 +6,8 @@ async function executorFn(options, context) {
6
6
  if (!context.projectName) throw new Error(`The Razorwind - Generate executor requires \`projectName\` on the context object.`);
7
7
  if (!context.projectName || !context.projectsConfigurations?.projects || !context.projectsConfigurations.projects[context.projectName] || !context.projectsConfigurations.projects[context.projectName]?.root) throw new Error(`The Razorwind - Generate executor requires \`projectsConfigurations\` on the context object.`);
8
8
  await execute(generator, {
9
- configFile: options.configFile,
10
- mode: options.mode,
11
- registryPath: options.registryPath,
12
- tokensPath: options.tokensPath
9
+ ...options,
10
+ mode: options.mode
13
11
  });
14
12
  return { success: true };
15
13
  }
@@ -20,10 +20,10 @@ export interface GenerateExecutorSchema {
20
20
  mode: string,
21
21
 
22
22
  /**
23
- * The path to the registry.json file
23
+ * The path to a directory containing component directories or files
24
24
  *
25
25
  */
26
- registryPath?: string,
26
+ componentsPath?: string,
27
27
 
28
28
  /**
29
29
  * The path to the tokens.json file
@@ -21,9 +21,9 @@
21
21
  "production"
22
22
  ]
23
23
  },
24
- "registryPath": {
24
+ "componentsPath": {
25
25
  "type": "string",
26
- "description": "The path to the registry.json file"
26
+ "description": "The path to a directory containing component directories or files"
27
27
  },
28
28
  "tokensPath": {
29
29
  "type": "string",
@@ -0,0 +1,7 @@
1
+ import { Tree } from "@nx/devkit";
2
+ import { SyncGeneratorResult } from "nx/src/utils/sync-generators";
3
+ //#region src/generators/sync/generator.d.ts
4
+ declare function syncGenerator(tree: Tree): Promise<SyncGeneratorResult>;
5
+ //#endregion
6
+ export { syncGenerator as default, syncGenerator };
7
+ //# sourceMappingURL=generator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.mts","names":[],"sources":["../../../src/generators/sync/generator.ts"],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import { Tree } from "@nx/devkit";
2
+ import { SyncGeneratorResult } from "nx/src/utils/sync-generators";
3
+ //#region src/generators/sync/generator.d.ts
4
+ declare function syncGenerator(tree: Tree): Promise<SyncGeneratorResult>;
5
+ //#endregion
6
+ export { syncGenerator as default, syncGenerator };
7
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","names":[],"sources":["../../../src/generators/sync/generator.ts"],"mappings":";;;iBA6HsB,cAAc,MAAM,OAAO,QAAQ"}
@@ -0,0 +1,121 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let _power_plant_core = require("@power-plant/core");
30
+ let _razorwind_core = require("@razorwind/core");
31
+ let _nx_devkit = require("@nx/devkit");
32
+ let _power_plant_noop_output = require("@power-plant/noop-output");
33
+ _power_plant_noop_output = __toESM(_power_plant_noop_output);
34
+ let node_path = require("node:path");
35
+
36
+ //#region src/generators/sync/generator.ts
37
+ const GENERATOR_NAME = "@razorwind/nx:sync";
38
+ const GENERATE_EXECUTOR = "@razorwind/nx:generate";
39
+ const CONFIG_FILE_NAMES = [
40
+ "razorwind.config.ts",
41
+ "razorwind.config.js",
42
+ "razorwind.config.mts",
43
+ "razorwind.config.mjs"
44
+ ];
45
+ const DEFAULT_OUT_OF_SYNC_MESSAGE = "Razorwind generated files are out of sync. Run `nx sync` to regenerate.";
46
+ function resolveConfigFileToken(configFile, projectRoot) {
47
+ return configFile.replaceAll("{projectRoot}", projectRoot);
48
+ }
49
+ function findDefaultConfigFile(tree, projectRoot) {
50
+ return CONFIG_FILE_NAMES.map((name) => (0, _nx_devkit.joinPathFragments)(projectRoot, name)).find((path) => tree.exists(path));
51
+ }
52
+ function getDocumentContent(document) {
53
+ return (document.chunks ?? []).map((chunk) => chunk.content ?? "").join("");
54
+ }
55
+ function toWorkspaceRelativePath(tree, filePath) {
56
+ if ((0, node_path.isAbsolute)(filePath)) return (0, node_path.relative)(tree.root, filePath).split(/[/\\]/).join("/");
57
+ return filePath;
58
+ }
59
+ function writeDocumentIfChanged(tree, document) {
60
+ const path = toWorkspaceRelativePath(tree, document.path);
61
+ const content = getDocumentContent(document);
62
+ if (tree.exists(path) && tree.read(path, "utf-8") === content) return;
63
+ tree.write(path, content);
64
+ return path;
65
+ }
66
+ async function generateForProject(tree, options) {
67
+ const documents = await (0, _power_plant_core.execute)(_razorwind_core.generator, {
68
+ configFile: options.configFile,
69
+ mode: options.mode,
70
+ componentsPath: options.componentsPath,
71
+ tokensPath: options.tokensPath,
72
+ cwd: tree.root,
73
+ output: _power_plant_noop_output.default
74
+ });
75
+ const changed = [];
76
+ for (const document of Object.values(documents ?? {})) {
77
+ const path = writeDocumentIfChanged(tree, document);
78
+ if (path) changed.push(path);
79
+ }
80
+ return changed;
81
+ }
82
+ async function syncGenerator(tree) {
83
+ const generatorOptions = (0, _nx_devkit.readNxJson)(tree)?.sync?.generatorOptions?.[GENERATOR_NAME] ?? {};
84
+ const defaultMode = generatorOptions.mode ?? "production";
85
+ const outOfSyncMessage = generatorOptions.outOfSyncMessage ?? DEFAULT_OUT_OF_SYNC_MESSAGE;
86
+ const projectGraph = await (0, _nx_devkit.createProjectGraphAsync)();
87
+ const outOfSyncDetails = [];
88
+ const processedConfigFiles = /* @__PURE__ */ new Set();
89
+ for (const project of Object.values(projectGraph.nodes)) {
90
+ const projectRoot = project.data.root;
91
+ const generateTarget = project.data.targets?.generate;
92
+ let configFile;
93
+ let mode = defaultMode;
94
+ let componentsPath;
95
+ let tokensPath;
96
+ if (generateTarget?.executor === GENERATE_EXECUTOR) {
97
+ mode = generateTarget.configurations?.[generateTarget.defaultConfiguration ?? "production"]?.mode ?? generateTarget.options?.mode ?? defaultMode;
98
+ componentsPath = generateTarget.options?.componentsPath;
99
+ tokensPath = generateTarget.options?.tokensPath;
100
+ const configured = generateTarget.options?.configFile ? resolveConfigFileToken(generateTarget.options.configFile, projectRoot) : void 0;
101
+ configFile = configured && tree.exists(configured) ? configured : findDefaultConfigFile(tree, projectRoot);
102
+ } else configFile = findDefaultConfigFile(tree, projectRoot);
103
+ if (!configFile || processedConfigFiles.has(configFile)) continue;
104
+ processedConfigFiles.add(configFile);
105
+ const changed = await generateForProject(tree, {
106
+ configFile,
107
+ mode,
108
+ componentsPath,
109
+ tokensPath
110
+ });
111
+ outOfSyncDetails.push(...changed);
112
+ }
113
+ return {
114
+ outOfSyncMessage,
115
+ outOfSyncDetails
116
+ };
117
+ }
118
+
119
+ //#endregion
120
+ exports.default = syncGenerator;
121
+ exports.syncGenerator = syncGenerator;
@@ -0,0 +1,92 @@
1
+ import { execute } from "@power-plant/core";
2
+ import { generator } from "@razorwind/core";
3
+ import { createProjectGraphAsync, joinPathFragments, readNxJson } from "@nx/devkit";
4
+ import noop from "@power-plant/noop-output";
5
+ import { isAbsolute, relative } from "node:path";
6
+
7
+ //#region src/generators/sync/generator.ts
8
+ const GENERATOR_NAME = "@razorwind/nx:sync";
9
+ const GENERATE_EXECUTOR = "@razorwind/nx:generate";
10
+ const CONFIG_FILE_NAMES = [
11
+ "razorwind.config.ts",
12
+ "razorwind.config.js",
13
+ "razorwind.config.mts",
14
+ "razorwind.config.mjs"
15
+ ];
16
+ const DEFAULT_OUT_OF_SYNC_MESSAGE = "Razorwind generated files are out of sync. Run `nx sync` to regenerate.";
17
+ function resolveConfigFileToken(configFile, projectRoot) {
18
+ return configFile.replaceAll("{projectRoot}", projectRoot);
19
+ }
20
+ function findDefaultConfigFile(tree, projectRoot) {
21
+ return CONFIG_FILE_NAMES.map((name) => joinPathFragments(projectRoot, name)).find((path) => tree.exists(path));
22
+ }
23
+ function getDocumentContent(document) {
24
+ return (document.chunks ?? []).map((chunk) => chunk.content ?? "").join("");
25
+ }
26
+ function toWorkspaceRelativePath(tree, filePath) {
27
+ if (isAbsolute(filePath)) return relative(tree.root, filePath).split(/[/\\]/).join("/");
28
+ return filePath;
29
+ }
30
+ function writeDocumentIfChanged(tree, document) {
31
+ const path = toWorkspaceRelativePath(tree, document.path);
32
+ const content = getDocumentContent(document);
33
+ if (tree.exists(path) && tree.read(path, "utf-8") === content) return;
34
+ tree.write(path, content);
35
+ return path;
36
+ }
37
+ async function generateForProject(tree, options) {
38
+ const documents = await execute(generator, {
39
+ configFile: options.configFile,
40
+ mode: options.mode,
41
+ componentsPath: options.componentsPath,
42
+ tokensPath: options.tokensPath,
43
+ cwd: tree.root,
44
+ output: noop
45
+ });
46
+ const changed = [];
47
+ for (const document of Object.values(documents ?? {})) {
48
+ const path = writeDocumentIfChanged(tree, document);
49
+ if (path) changed.push(path);
50
+ }
51
+ return changed;
52
+ }
53
+ async function syncGenerator(tree) {
54
+ const generatorOptions = readNxJson(tree)?.sync?.generatorOptions?.[GENERATOR_NAME] ?? {};
55
+ const defaultMode = generatorOptions.mode ?? "production";
56
+ const outOfSyncMessage = generatorOptions.outOfSyncMessage ?? DEFAULT_OUT_OF_SYNC_MESSAGE;
57
+ const projectGraph = await createProjectGraphAsync();
58
+ const outOfSyncDetails = [];
59
+ const processedConfigFiles = /* @__PURE__ */ new Set();
60
+ for (const project of Object.values(projectGraph.nodes)) {
61
+ const projectRoot = project.data.root;
62
+ const generateTarget = project.data.targets?.generate;
63
+ let configFile;
64
+ let mode = defaultMode;
65
+ let componentsPath;
66
+ let tokensPath;
67
+ if (generateTarget?.executor === GENERATE_EXECUTOR) {
68
+ mode = generateTarget.configurations?.[generateTarget.defaultConfiguration ?? "production"]?.mode ?? generateTarget.options?.mode ?? defaultMode;
69
+ componentsPath = generateTarget.options?.componentsPath;
70
+ tokensPath = generateTarget.options?.tokensPath;
71
+ const configured = generateTarget.options?.configFile ? resolveConfigFileToken(generateTarget.options.configFile, projectRoot) : void 0;
72
+ configFile = configured && tree.exists(configured) ? configured : findDefaultConfigFile(tree, projectRoot);
73
+ } else configFile = findDefaultConfigFile(tree, projectRoot);
74
+ if (!configFile || processedConfigFiles.has(configFile)) continue;
75
+ processedConfigFiles.add(configFile);
76
+ const changed = await generateForProject(tree, {
77
+ configFile,
78
+ mode,
79
+ componentsPath,
80
+ tokensPath
81
+ });
82
+ outOfSyncDetails.push(...changed);
83
+ }
84
+ return {
85
+ outOfSyncMessage,
86
+ outOfSyncDetails
87
+ };
88
+ }
89
+
90
+ //#endregion
91
+ export { syncGenerator as default, syncGenerator };
92
+ //# sourceMappingURL=generator.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.mjs","names":[],"sources":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+
2
+ // Generated by powerlines
3
+ // Do not edit this file directly
4
+
5
+ export interface SyncGeneratorSchema {
6
+ /**
7
+ * Mode
8
+ *
9
+ * The mode to use when running Razorwind generate
10
+ *
11
+ * @default "production"
12
+ *
13
+ * @enum development,production
14
+ */
15
+ mode?: string,
16
+
17
+ /**
18
+ * Out of Sync Message
19
+ *
20
+ * The message to display when Razorwind generated files are out of sync
21
+ *
22
+ * @default "Razorwind generated files are out of sync. Run `nx sync` to regenerate."
23
+ */
24
+ outOfSyncMessage?: string,
25
+ }
26
+
@@ -0,0 +1,31 @@
1
+ {
2
+ "id": "#",
3
+ "title": "Sync Generator",
4
+ "description": "A type definition for the Razorwind - Sync generator's options",
5
+ "required": [],
6
+ "properties": {
7
+ "mode": {
8
+ "title": "Mode",
9
+ "type": "string",
10
+ "description": "The mode to use when running Razorwind generate",
11
+ "enum": [
12
+ "development",
13
+ "production"
14
+ ],
15
+ "id": "#mode",
16
+ "default": "production"
17
+ },
18
+ "outOfSyncMessage": {
19
+ "title": "Out of Sync Message",
20
+ "type": "string",
21
+ "description": "The message to display when Razorwind generated files are out of sync",
22
+ "id": "#outOfSyncMessage",
23
+ "default": "Razorwind generated files are out of sync. Run `nx sync` to regenerate."
24
+ }
25
+ },
26
+ "type": "object",
27
+ "default": {
28
+ "mode": "production",
29
+ "outOfSyncMessage": "Razorwind generated files are out of sync. Run `nx sync` to regenerate."
30
+ }
31
+ }
@@ -20,11 +20,11 @@ The path to the configuration file
20
20
  The mode to use
21
21
 
22
22
 
23
- ## `registryPath`
23
+ ## `componentsPath`
24
24
  - **Type**: `string`
25
25
 
26
26
 
27
- The path to the registry.json file
27
+ The path to a directory containing component directories or files
28
28
 
29
29
 
30
30
  ## `tokensPath`
@@ -0,0 +1,26 @@
1
+
2
+ <!-- Generated by powerlines -->
3
+ <!-- Do not edit this file directly -->
4
+
5
+ # Sync Generator
6
+
7
+ ## `mode`
8
+ - **Type**: `string`
9
+ - **Default**: `"production"`
10
+
11
+ > Mode
12
+
13
+
14
+ The mode to use when running Razorwind generate
15
+
16
+
17
+ ## `outOfSyncMessage`
18
+ - **Type**: `string`
19
+ - **Default**: `"Razorwind generated files are out of sync. Run `nx sync` to regenerate."`
20
+
21
+ > Out of Sync Message
22
+
23
+
24
+ The message to display when Razorwind generated files are out of sync
25
+
26
+
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "generators": {
4
+ "sync": {
5
+ "factory": "./dist/generators/sync/generator",
6
+ "schema": "./dist/generators/sync/schema.json",
7
+ "description": "Synchronize Razorwind generated files across the workspace",
8
+ "alias": ["generate-sync"],
9
+ "hidden": true
10
+ }
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@razorwind/nx",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "A Nx plugin to support Razorwind development in Nx monorepos.",
5
5
  "repository": {
6
6
  "type": "github",
@@ -12,7 +12,9 @@
12
12
  "./package.json": "./package.json",
13
13
  "./docs/*": "./docs/*",
14
14
  "./executors.json": "./executors.json",
15
+ "./generators.json": "./generators.json",
15
16
  "./executors/*/schema.json": "./dist/executors/*/schema.json",
17
+ "./generators/*/schema.json": "./dist/generators/*/schema.json",
16
18
  ".": {
17
19
  "import": {
18
20
  "types": "./dist/index.d.mts",
@@ -57,6 +59,20 @@
57
59
  "default": "./dist/executors/*/untyped.js"
58
60
  }
59
61
  },
62
+ "./generators/*/untyped": {
63
+ "import": {
64
+ "types": "./dist/generators/*/untyped.d.mts",
65
+ "default": "./dist/generators/*/untyped.mjs"
66
+ },
67
+ "require": {
68
+ "types": "./dist/generators/*/untyped.d.ts",
69
+ "default": "./dist/generators/*/untyped.js"
70
+ },
71
+ "default": {
72
+ "types": "./dist/generators/*/untyped.d.ts",
73
+ "default": "./dist/generators/*/untyped.js"
74
+ }
75
+ },
60
76
  "./executors/*/executor": {
61
77
  "import": {
62
78
  "types": "./dist/executors/*/executor.d.mts",
@@ -71,16 +87,32 @@
71
87
  "default": "./dist/executors/*/executor.js"
72
88
  }
73
89
  },
90
+ "./generators/*/generator": {
91
+ "import": {
92
+ "types": "./dist/generators/*/generator.d.mts",
93
+ "default": "./dist/generators/*/generator.mjs"
94
+ },
95
+ "require": {
96
+ "types": "./dist/generators/*/generator.d.ts",
97
+ "default": "./dist/generators/*/generator.js"
98
+ },
99
+ "default": {
100
+ "types": "./dist/generators/*/generator.d.ts",
101
+ "default": "./dist/generators/*/generator.js"
102
+ }
103
+ },
74
104
  "./executors/*/executor.js": "./dist/executors/*/executor.js",
75
- "./executors/*/executor.mjs": "./dist/executors/*/executor.mjs"
105
+ "./executors/*/executor.mjs": "./dist/executors/*/executor.mjs",
106
+ "./generators/*/generator.js": "./dist/generators/*/generator.js",
107
+ "./generators/*/generator.mjs": "./dist/generators/*/generator.mjs"
76
108
  },
77
109
  "main": "dist/index.js",
78
110
  "module": "dist/index.mjs",
79
111
  "typings": "dist/index.d.ts",
80
112
  "dependencies": {
81
113
  "@nx/devkit": "23.1.0",
82
- "@power-plant/core": "^0.0.40",
83
- "@razorwind/core": "^0.0.10",
114
+ "@power-plant/core": "^0.0.52",
115
+ "@razorwind/core": "^0.0.12",
84
116
  "@stryke/hash": "^0.13.52",
85
117
  "@stryke/path": "^0.29.25",
86
118
  "@stryke/string-format": "^0.17.40",
@@ -90,8 +122,8 @@
90
122
  "untyped": "^2.0.0"
91
123
  },
92
124
  "devDependencies": {
93
- "@powerlines/plugin-tsdown": "^0.1.561",
94
- "@powerlines/plugin-untyped": "^0.2.575",
125
+ "@powerlines/plugin-tsdown": "^0.1.571",
126
+ "@powerlines/plugin-untyped": "^0.2.585",
95
127
  "@types/node": "^25.9.5",
96
128
  "eslint-flat-config-utils": "^3.2.0",
97
129
  "jsonc-eslint-parser": "^3.1.0"
@@ -99,5 +131,6 @@
99
131
  "peerDependencies": { "nx": ">=23.0.0" },
100
132
  "publishConfig": { "access": "public" },
101
133
  "executors": "./executors.json",
102
- "gitHead": "35f5d31c337fdedeb003c0d9eca72949d4305437"
134
+ "generators": "./generators.json",
135
+ "gitHead": "2c7a147ef95f662154e13be38137f629a71cff62"
103
136
  }