@xpack/xpm-lib 3.1.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +16 -212
  2. package/dist/classes/actions.d.ts +58 -0
  3. package/dist/classes/actions.d.ts.map +1 -0
  4. package/dist/classes/actions.js +250 -0
  5. package/dist/classes/actions.js.map +1 -0
  6. package/dist/classes/build-configurations.d.ts +78 -0
  7. package/dist/classes/build-configurations.d.ts.map +1 -0
  8. package/dist/classes/build-configurations.js +489 -0
  9. package/dist/classes/build-configurations.js.map +1 -0
  10. package/dist/classes/combinations-generator.d.ts +19 -0
  11. package/dist/classes/combinations-generator.d.ts.map +1 -0
  12. package/dist/classes/combinations-generator.js +48 -0
  13. package/dist/classes/combinations-generator.js.map +1 -0
  14. package/dist/classes/data-model.d.ts +21 -0
  15. package/dist/classes/data-model.d.ts.map +1 -0
  16. package/dist/classes/data-model.js +47 -0
  17. package/dist/classes/data-model.js.map +1 -0
  18. package/dist/classes/errors.d.ts +13 -0
  19. package/dist/classes/errors.d.ts.map +1 -0
  20. package/dist/classes/errors.js +13 -0
  21. package/dist/classes/errors.js.map +1 -0
  22. package/dist/classes/init-template-base.d.ts +47 -0
  23. package/dist/classes/init-template-base.d.ts.map +1 -0
  24. package/dist/classes/init-template-base.js +358 -0
  25. package/dist/classes/init-template-base.js.map +1 -0
  26. package/dist/classes/liquid-drop.d.ts +28 -0
  27. package/dist/classes/liquid-drop.d.ts.map +1 -0
  28. package/dist/classes/liquid-drop.js +70 -0
  29. package/dist/classes/liquid-drop.js.map +1 -0
  30. package/dist/classes/liquid-engine.d.ts +7 -0
  31. package/dist/classes/liquid-engine.d.ts.map +1 -0
  32. package/dist/classes/liquid-engine.js +72 -0
  33. package/dist/classes/liquid-engine.js.map +1 -0
  34. package/dist/classes/package.d.ts +31 -0
  35. package/dist/classes/package.d.ts.map +1 -0
  36. package/dist/classes/package.js +268 -0
  37. package/dist/classes/package.js.map +1 -0
  38. package/dist/classes/platform-detector.d.ts +14 -0
  39. package/dist/classes/platform-detector.d.ts.map +1 -0
  40. package/dist/classes/platform-detector.js +26 -0
  41. package/dist/classes/platform-detector.js.map +1 -0
  42. package/dist/classes/policies.d.ts +14 -0
  43. package/dist/classes/policies.d.ts.map +1 -0
  44. package/dist/classes/policies.js +20 -0
  45. package/dist/classes/policies.js.map +1 -0
  46. package/dist/classes/template-expander.d.ts +29 -0
  47. package/dist/classes/template-expander.d.ts.map +1 -0
  48. package/dist/classes/template-expander.js +62 -0
  49. package/dist/classes/template-expander.js.map +1 -0
  50. package/dist/data/substitutions-variables.d.ts +43 -0
  51. package/dist/data/substitutions-variables.d.ts.map +1 -0
  52. package/dist/{lib → data}/substitutions-variables.js +1 -16
  53. package/dist/data/substitutions-variables.js.map +1 -0
  54. package/dist/functions/chmod-recursively.d.ts +9 -0
  55. package/dist/functions/chmod-recursively.d.ts.map +1 -0
  56. package/dist/functions/chmod-recursively.js +66 -0
  57. package/dist/functions/chmod-recursively.js.map +1 -0
  58. package/dist/functions/filter-paths.d.ts +5 -0
  59. package/dist/functions/filter-paths.d.ts.map +1 -0
  60. package/dist/functions/filter-paths.js +16 -0
  61. package/dist/functions/filter-paths.js.map +1 -0
  62. package/dist/functions/is-something.d.ts +9 -0
  63. package/dist/functions/is-something.d.ts.map +1 -0
  64. package/dist/functions/is-something.js +25 -0
  65. package/dist/functions/is-something.js.map +1 -0
  66. package/dist/functions/matrix-expander.d.ts +17 -0
  67. package/dist/functions/matrix-expander.d.ts.map +1 -0
  68. package/dist/functions/matrix-expander.js +52 -0
  69. package/dist/functions/matrix-expander.js.map +1 -0
  70. package/dist/functions/perform-substitutions.d.ts +12 -0
  71. package/dist/functions/perform-substitutions.d.ts.map +1 -0
  72. package/dist/functions/perform-substitutions.js +76 -0
  73. package/dist/functions/perform-substitutions.js.map +1 -0
  74. package/dist/functions/utils.d.ts +8 -0
  75. package/dist/functions/utils.d.ts.map +1 -0
  76. package/dist/functions/utils.js +16 -0
  77. package/dist/functions/utils.js.map +1 -0
  78. package/dist/index.d.ts +22 -15
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +22 -29
  81. package/dist/index.js.map +1 -1
  82. package/dist/{lib/types.d.ts → types/json.d.ts} +31 -22
  83. package/dist/types/json.d.ts.map +1 -0
  84. package/dist/types/json.js +2 -0
  85. package/dist/types/json.js.map +1 -0
  86. package/dist/types/xpm-init-template.d.ts +21 -0
  87. package/dist/types/xpm-init-template.d.ts.map +1 -0
  88. package/dist/types/xpm-init-template.js +2 -0
  89. package/dist/types/xpm-init-template.js.map +1 -0
  90. package/dist/types/xpm.d.ts +16 -0
  91. package/dist/types/xpm.d.ts.map +1 -0
  92. package/dist/types/xpm.js +2 -0
  93. package/dist/types/xpm.js.map +1 -0
  94. package/package.json +53 -44
  95. package/src/CODE-REVIEW.md +2167 -0
  96. package/src/README.md +393 -6
  97. package/src/classes/actions.ts +1157 -0
  98. package/src/classes/build-configurations.ts +2127 -0
  99. package/src/classes/combinations-generator.ts +331 -0
  100. package/src/classes/data-model.ts +337 -0
  101. package/src/classes/errors.ts +105 -0
  102. package/src/classes/init-template-base.ts +1028 -0
  103. package/src/classes/liquid-drop.ts +376 -0
  104. package/src/classes/liquid-engine.ts +249 -0
  105. package/src/classes/package.ts +765 -0
  106. package/src/classes/platform-detector.ts +237 -0
  107. package/src/classes/policies.ts +200 -0
  108. package/src/classes/template-expander.ts +330 -0
  109. package/src/data/substitutions-variables.ts +390 -0
  110. package/src/functions/chmod-recursively.ts +195 -0
  111. package/src/functions/filter-paths.ts +126 -0
  112. package/src/functions/is-something.ts +223 -0
  113. package/src/functions/matrix-expander.ts +172 -0
  114. package/src/functions/perform-substitutions.ts +253 -0
  115. package/src/functions/utils.ts +151 -0
  116. package/src/index.ts +72 -19
  117. package/src/types/json.ts +519 -0
  118. package/src/types/xpm-init-template.ts +282 -0
  119. package/src/types/xpm.ts +162 -0
  120. package/dist/lib/chmod-recursive.d.ts +0 -7
  121. package/dist/lib/chmod-recursive.d.ts.map +0 -1
  122. package/dist/lib/chmod-recursive.js +0 -81
  123. package/dist/lib/chmod-recursive.js.map +0 -1
  124. package/dist/lib/errors.d.ts +0 -11
  125. package/dist/lib/errors.d.ts.map +0 -1
  126. package/dist/lib/errors.js +0 -26
  127. package/dist/lib/errors.js.map +0 -1
  128. package/dist/lib/functions/chmod-recursive.d.ts +0 -7
  129. package/dist/lib/functions/chmod-recursive.d.ts.map +0 -1
  130. package/dist/lib/functions/chmod-recursive.js +0 -81
  131. package/dist/lib/functions/chmod-recursive.js.map +0 -1
  132. package/dist/lib/functions/perform-substitutions.d.ts +0 -20
  133. package/dist/lib/functions/perform-substitutions.d.ts.map +0 -1
  134. package/dist/lib/functions/perform-substitutions.js +0 -85
  135. package/dist/lib/functions/perform-substitutions.js.map +0 -1
  136. package/dist/lib/functions/utils.d.ts +0 -30
  137. package/dist/lib/functions/utils.d.ts.map +0 -1
  138. package/dist/lib/functions/utils.js +0 -70
  139. package/dist/lib/functions/utils.js.map +0 -1
  140. package/dist/lib/init-template-base.d.ts +0 -46
  141. package/dist/lib/init-template-base.d.ts.map +0 -1
  142. package/dist/lib/init-template-base.js +0 -281
  143. package/dist/lib/init-template-base.js.map +0 -1
  144. package/dist/lib/liquid-actions.d.ts +0 -37
  145. package/dist/lib/liquid-actions.d.ts.map +0 -1
  146. package/dist/lib/liquid-actions.js +0 -148
  147. package/dist/lib/liquid-actions.js.map +0 -1
  148. package/dist/lib/liquid-build-configurations.d.ts +0 -47
  149. package/dist/lib/liquid-build-configurations.d.ts.map +0 -1
  150. package/dist/lib/liquid-build-configurations.js +0 -282
  151. package/dist/lib/liquid-build-configurations.js.map +0 -1
  152. package/dist/lib/liquid-drop.d.ts +0 -13
  153. package/dist/lib/liquid-drop.d.ts.map +0 -1
  154. package/dist/lib/liquid-drop.js +0 -56
  155. package/dist/lib/liquid-drop.js.map +0 -1
  156. package/dist/lib/liquid-engine.d.ts +0 -5
  157. package/dist/lib/liquid-engine.d.ts.map +0 -1
  158. package/dist/lib/liquid-engine.js +0 -85
  159. package/dist/lib/liquid-engine.js.map +0 -1
  160. package/dist/lib/liquid-package.d.ts +0 -17
  161. package/dist/lib/liquid-package.d.ts.map +0 -1
  162. package/dist/lib/liquid-package.js +0 -70
  163. package/dist/lib/liquid-package.js.map +0 -1
  164. package/dist/lib/package.d.ts +0 -66
  165. package/dist/lib/package.d.ts.map +0 -1
  166. package/dist/lib/package.js +0 -700
  167. package/dist/lib/package.js.map +0 -1
  168. package/dist/lib/perform-substitutions.d.ts +0 -20
  169. package/dist/lib/perform-substitutions.d.ts.map +0 -1
  170. package/dist/lib/perform-substitutions.js +0 -85
  171. package/dist/lib/perform-substitutions.js.map +0 -1
  172. package/dist/lib/policies.d.ts +0 -14
  173. package/dist/lib/policies.d.ts.map +0 -1
  174. package/dist/lib/policies.js +0 -33
  175. package/dist/lib/policies.js.map +0 -1
  176. package/dist/lib/substitutions-variables.d.ts +0 -117
  177. package/dist/lib/substitutions-variables.d.ts.map +0 -1
  178. package/dist/lib/substitutions-variables.js.map +0 -1
  179. package/dist/lib/types.d.ts.map +0 -1
  180. package/dist/lib/types.js +0 -13
  181. package/dist/lib/types.js.map +0 -1
  182. package/dist/lib/utils.d.ts +0 -30
  183. package/dist/lib/utils.d.ts.map +0 -1
  184. package/dist/lib/utils.js +0 -70
  185. package/dist/lib/utils.js.map +0 -1
  186. package/dist/tsconfig.tsbuildinfo +0 -1
  187. package/src/lib/errors.ts +0 -29
  188. package/src/lib/functions/chmod-recursive.ts +0 -103
  189. package/src/lib/functions/perform-substitutions.ts +0 -116
  190. package/src/lib/functions/utils.ts +0 -88
  191. package/src/lib/init-template-base.ts +0 -408
  192. package/src/lib/liquid-actions.ts +0 -223
  193. package/src/lib/liquid-build-configurations.ts +0 -433
  194. package/src/lib/liquid-drop.ts +0 -99
  195. package/src/lib/liquid-engine.ts +0 -135
  196. package/src/lib/liquid-package.ts +0 -108
  197. package/src/lib/package.ts +0 -947
  198. package/src/lib/policies.ts +0 -51
  199. package/src/lib/substitutions-variables.ts +0 -177
  200. package/src/lib/types.ts +0 -109
  201. package/src/package.json +0 -3
  202. package/src/tsconfig.json +0 -10
@@ -0,0 +1,390 @@
1
+ /*
2
+ * This file is part of the xPack project (http://xpack.github.io).
3
+ * Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
4
+ *
5
+ * Permission to use, copy, modify, and/or distribute this software
6
+ * for any purpose is hereby granted, under the terms of the MIT license.
7
+ *
8
+ * If a copy of the license was not distributed with this file, it can
9
+ * be obtained from https://opensource.org/license/mit.
10
+ */
11
+
12
+ // ----------------------------------------------------------------------------
13
+
14
+ import * as os from 'node:os'
15
+ import * as path from 'node:path'
16
+
17
+ // ============================================================================
18
+
19
+ /**
20
+ * Represents a map of substitution values used by Liquid templates.
21
+ *
22
+ * @remarks
23
+ * Values can be strings for simple substitutions or arrays for multi-line
24
+ * content. Array values are typically joined with newlines when rendered.
25
+ *
26
+ * Common use cases:
27
+ *
28
+ * <ul>
29
+ * <li><b>Properties:</b> User-defined configuration values from
30
+ * <code>xpack.properties.</code></li>
31
+ * <li><b>Matrix parameters:</b> Template expansion variables from
32
+ * <code>matrix</code> definitions.</li>
33
+ * <li><b>Configuration data:</b> Build-specific settings and metadata.</li>
34
+ * </ul>
35
+ *
36
+ * Templates access these values via namespaces like `properties.foo`,
37
+ * `matrix.arch`, etc., with the Liquid Drop pattern providing lazy
38
+ * evaluation and nested substitution support.
39
+ */
40
+ export type LiquidSubstitutionsStrings = Record<string, string | string[]>
41
+
42
+ /**
43
+ * Defines the substitution variables available to Liquid templates.
44
+ *
45
+ * @remarks
46
+ * This interface mirrors a subset of Node.js environment, operating
47
+ * system, and path information, along with package-specific configuration
48
+ * values.
49
+ *
50
+ * Variable hierarchy and scoping:
51
+ *
52
+ * <ol>
53
+ * <li><b>Base variables</b> (<code>env</code>, <code>os</code>,
54
+ * <code>path</code>): Available globally, initialized once
55
+ * from Node.js runtime at startup.</li>
56
+ * <li><b>Package variables:</b> Added when processing
57
+ * <code>package.json</code>, contains
58
+ * package metadata accessible via <code>package.name</code>,
59
+ * <code>package.version</code>,
60
+ * etc.</li>
61
+ * <li><b>Properties:</b> User-defined values from
62
+ * <code>xpack.properties</code>, accessible via
63
+ * <code>properties.key</code>.</li>
64
+ * <li><b>Configuration:</b> Build configuration metadata, available when
65
+ * processing configuration-specific templates via
66
+ * <code>configuration.name</code>,
67
+ * etc.</li>
68
+ * <li><b>Matrix:</b> Template expansion parameters, scoped to individual
69
+ * expanded instances, accessible via <code>matrix.key</code>.</li>
70
+ * </ol>
71
+ *
72
+ * Variables are inherited and extended through the hierarchy:
73
+ *
74
+ * <ul>
75
+ * <li>package actions use package properties</li>
76
+ * <li>configuration actions use:
77
+ * <ul>
78
+ * <li>package actions</li>
79
+ * <li>actions inherited from parent configurations, recursively,
80
+ * in order of inheritance</li>
81
+ * <li>configuration properties</li>
82
+ * </ul>
83
+ * </li>
84
+ * </ul>
85
+ *
86
+ * This ensures templates have access to appropriate
87
+ * context without exposing unrelated data.
88
+ */
89
+ export interface LiquidSubstitutionsVariables {
90
+ /**
91
+ * Process environment variables from the current execution context.
92
+ *
93
+ * @remarks
94
+ * Provides access to all environment variables via `env.VARIABLE_NAME`
95
+ * in templates. Common uses include accessing `PATH`, `HOME`, `USER`, or
96
+ * custom variables set by build scripts.
97
+ *
98
+ * See {@link https://nodejs.org/dist/latest-v16.x/docs/api/process.html#process_process_env | Node.js process.env documentation}
99
+ */
100
+ env: NodeJS.ProcessEnv
101
+ /**
102
+ * Operating system information from Node.js os module.
103
+ *
104
+ * @remarks
105
+ * Provides platform detection and system information for cross-platform
106
+ * template logic. Common uses include conditional compilation, path
107
+ * construction, and platform-specific configuration.
108
+ *
109
+ * Key properties for cross-platform templates:
110
+ *
111
+ * <ul>
112
+ * <li><code>os.platform</code>: Detect OS ('darwin', 'linux', 'win32').</li>
113
+ * <li><code>os.arch</code>: Detect CPU architecture ('x64', 'arm64',
114
+ * etc.).</li>
115
+ * <li><code>os.EOL</code>: Use correct line endings for generated files.</li>
116
+ * <li><code>os.homedir</code>: Reference user's home folder portably.</li>
117
+ * </ul>
118
+ *
119
+ * See {@link https://nodejs.org/dist/latest-v16.x/docs/api/os.html | Node.js os module documentation}
120
+ */
121
+ os: {
122
+ /**
123
+ * The operating system-specific end-of-line marker.
124
+ * <ul>
125
+ * <li><code>\\n</code> on POSIX</li>
126
+ * <li><code>\\r\\n</code> on Windows</li>
127
+ * </ul>
128
+ */
129
+ EOL: string
130
+ /**
131
+ * Possible values are 'arm', 'arm64', 'ia32', 'mips', 'mipsel',
132
+ * 'ppc', 'ppc64', 's390', 's390x', 'x32', and 'x64'.
133
+ */
134
+ arch: string
135
+ /**
136
+ * Contains commonly used operating system-specific constants
137
+ * for error codes, process signals, and so on. The specific
138
+ * constants defined are described in
139
+ * {@link https://nodejs.org/dist/latest-v16.x/docs/api/os.html#os_os_constants_1 | OS constants}
140
+ */
141
+ constants: {
142
+ signals: Record<string, number>
143
+ errno: Record<string, number>
144
+ }
145
+ /**
146
+ * An array of objects containing information about
147
+ * each logical CPU core.
148
+ */
149
+ cpus: os.CpuInfo[]
150
+ /**
151
+ * A string identifying the endianness of the CPU
152
+ * for which the Node.js binary was compiled.
153
+ *
154
+ * Possible values are 'BE' for big endian and 'LE' for little endian.
155
+ */
156
+ endianness: 'BE' | 'LE'
157
+ /**
158
+ * The string path of the current user's home folder.
159
+ */
160
+ homedir: string
161
+ /**
162
+ * The host name of the operating system as a string.
163
+ */
164
+ hostname: string
165
+ /**
166
+ * A string identifying the operating system platform.
167
+ * Possible values are 'aix', 'darwin', 'freebsd', 'linux', 'openbsd',
168
+ * 'sunos', and 'win32'.
169
+ */
170
+ platform: NodeJS.Platform
171
+ /**
172
+ * The operating system as a string.
173
+ */
174
+ release: string
175
+ /**
176
+ * Returns the operating system's default folder for
177
+ * temporary files as a string.
178
+ */
179
+ tmpdir: string
180
+ /**
181
+ * Returns the operating system name as returned by uname(3).
182
+ * For example, it returns 'Linux' on Linux, 'Darwin' on macOS,
183
+ * and 'Windows_NT' on Windows.
184
+ */
185
+ type: string
186
+ /**
187
+ * Returns a string identifying the kernel version.
188
+ *
189
+ * On POSIX systems, the operating system release is determined
190
+ * by calling `uname(3)`. On Windows, `RtlGetVersion()` is used,
191
+ * and if it is not available, `GetVersionExW()` will be used.
192
+ */
193
+ version: string
194
+ }
195
+ /**
196
+ * Path separators and delimiters from Node.js path module.
197
+ *
198
+ * @remarks
199
+ * Provides platform-specific path constants for building file paths in
200
+ * templates. Use these to construct paths that work correctly on all
201
+ * platforms.
202
+ *
203
+ * Available constants:
204
+ *
205
+ * <ul>
206
+ * <li><code>path.sep</code>: Platform-specific path separator (/ or \).</li>
207
+ * <li><code>path.delimiter</code>: Platform-specific PATH delimiter
208
+ * (; or :).</li>
209
+ * <li><code>path.posix.*</code>: Force POSIX conventions regardless
210
+ of platform.</li>
211
+ * <li><code>path.win32.*</code>: Force Windows conventions regardless
212
+ of platform.</li>
213
+ * </ul>
214
+ *
215
+ * Note: For path manipulation, prefer using Liquid filters like
216
+ * `path_join`, `path_dirname`, etc., which handle cross-platform concerns
217
+ * automatically.
218
+ *
219
+ * See [Node.js path module documentation](https://nodejs.org/dist/latest-v16.x/docs/api/path.html)
220
+ */
221
+ path: {
222
+ /**
223
+ * Provides the platform-specific path delimiter:
224
+ * <ul>
225
+ * <li><code>;</code> for Windows</li>
226
+ * <li><code>:</code> for POSIX</li>
227
+ * </ul>
228
+ */
229
+ delimiter: string
230
+ /**
231
+ * Provides the platform-specific path segment separator:
232
+ * <ul>
233
+ * <li><code>\\</code> on Windows</li>
234
+ * <li><code>/</code> on POSIX</li>
235
+ * </ul>
236
+ */
237
+ sep: string
238
+ win32: {
239
+ delimiter: string
240
+ sep: string
241
+ }
242
+ posix: {
243
+ delimiter: string
244
+ sep: string
245
+ }
246
+ }
247
+ /**
248
+ * The package metadata exposed to Liquid templates.
249
+ *
250
+ * @remarks
251
+ * Contains the entire `package.json` content, allowing templates to access
252
+ * package name, version, description, dependencies, and xpack-specific
253
+ * metadata.
254
+ *
255
+ * Common template patterns:
256
+ *
257
+ * <ul>
258
+ * <li><code>\{\{ package.name \}\}</code>: Package name for generated
259
+ * files.</li>
260
+ * <li><code>\{\{ package.version \}\}</code>: Version string for
261
+ * documentation.</li>
262
+ * <li><code>\{\{ package.xpack.properties.key \}\}</code>: Access xpack
263
+ * properties.</li>
264
+ * </ul>
265
+ *
266
+ * Undefined when processing templates outside of a package context.
267
+ */
268
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
269
+ package?: any
270
+ /**
271
+ * The build configuration exposed to Liquid templates.
272
+ *
273
+ * @remarks
274
+ * Available only when processing templates within a build configuration
275
+ * context (actions, dependencies, properties belonging to a specific
276
+ * configuration).
277
+ *
278
+ * Contains the configuration name and all configuration properties,
279
+ * allowing templates to reference the current build context:
280
+ *
281
+ * <ul>
282
+ * <li><code>\{\{ configuration.name \}\}</code>: The build configuration
283
+ * name.</li>
284
+ * <li><code>\{\{ configuration.properties.key \}\}</code>:
285
+ * Configuration-specific
286
+ * settings.</li>
287
+ * </ul>
288
+ *
289
+ * Undefined when processing package-level templates.
290
+ */
291
+ configuration?: {
292
+ name: string
293
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
294
+ [key: string]: any
295
+ }
296
+ /**
297
+ * The properties map used for substitutions.
298
+ *
299
+ * @remarks
300
+ * User-defined configuration values from `xpack.properties`
301
+ * in `package.json`.
302
+ * Provides a namespace for custom template variables without polluting
303
+ * the global scope.
304
+ *
305
+ * Properties support nested substitutions: a property value can reference
306
+ * other properties, package metadata, or system variables using Liquid
307
+ * syntax. The Liquid Drop pattern ensures recursive evaluation.
308
+ *
309
+ * Access via `{{ properties.key }}` in templates.
310
+ */
311
+ properties: LiquidSubstitutionsStrings
312
+
313
+ /**
314
+ * Optional matrix parameters used for template expansion.
315
+ *
316
+ * @remarks
317
+ * Available only for actions or configurations generated from templates
318
+ * with matrix definitions. Each expanded instance receives a specific
319
+ * combination of matrix values.
320
+ *
321
+ * Matrix parameters enable generating multiple similar actions or
322
+ * configurations from a single template definition. For example, a matrix
323
+ * with `arch: ['x64', 'arm64']` and `os: ['linux', 'darwin']` generates
324
+ * 4 instances (x64-linux, x64-darwin, arm64-linux, arm64-darwin).
325
+ *
326
+ * Access via `{{ matrix.key }}` in templates. Scoped to the individual
327
+ * expanded instance, ensuring isolation between generated items.
328
+ */
329
+ matrix?: LiquidSubstitutionsStrings
330
+ }
331
+
332
+ /**
333
+ * The base substitution variables initialised from the current environment.
334
+ *
335
+ * @remarks
336
+ * This constant provides the foundation for all Liquid template processing,
337
+ * capturing the runtime environment once at module load time.
338
+ *
339
+ * Initialization strategy:
340
+ *
341
+ * <ol>
342
+ * <li><b>Environment variables:</b> Snapshot of process.env at load time.</li>
343
+ * <li><b>OS information:</b> OS specific definitions (platform, arch,
344
+ * etc.).</li>
345
+ * <li><b>Path constants:</b> Platform-specific separators and delimiters.</li>
346
+ * <li><b>Properties:</b> Placeholder for package-specific additions.</li>
347
+ * </ol>
348
+ *
349
+ * These base variables are shared across all template processing within the
350
+ * application and extended with package, configuration, and matrix variables
351
+ * as needed. The base object is typically spread into new contexts rather
352
+ * than mutated, preserving the original snapshot.
353
+ */
354
+
355
+ export const liquidSubstitutionsVariablesBase: LiquidSubstitutionsVariables = {
356
+ env: process.env,
357
+ os: {
358
+ EOL: os.EOL,
359
+ arch: os.arch(),
360
+ constants: {
361
+ signals: os.constants.signals,
362
+ errno: os.constants.errno,
363
+ },
364
+ cpus: os.cpus(),
365
+ endianness: os.endianness(),
366
+ homedir: os.homedir(),
367
+ hostname: os.hostname(),
368
+ platform: os.platform(),
369
+ release: os.release(),
370
+ tmpdir: os.tmpdir(),
371
+ type: os.type(),
372
+ // os.version() available since 12.x
373
+ version: os.version(),
374
+ },
375
+ path: {
376
+ delimiter: path.delimiter,
377
+ sep: path.sep,
378
+ win32: {
379
+ delimiter: path.win32.delimiter,
380
+ sep: path.win32.sep,
381
+ },
382
+ posix: {
383
+ delimiter: path.posix.delimiter,
384
+ sep: path.posix.sep,
385
+ },
386
+ },
387
+ properties: {},
388
+ }
389
+
390
+ // ----------------------------------------------------------------------------
@@ -0,0 +1,195 @@
1
+ /*
2
+ * This file is part of the xPack project (http://xpack.github.io).
3
+ * Copyright (c) 2017-2026 Liviu Ionescu. All rights reserved.
4
+ *
5
+ * Permission to use, copy, modify, and/or distribute this software
6
+ * for any purpose is hereby granted, under the terms of the MIT license.
7
+ *
8
+ * If a copy of the license was not distributed with this file, it can
9
+ * be obtained from https://opensource.org/license/mit.
10
+ */
11
+
12
+ // ----------------------------------------------------------------------------
13
+
14
+ // https://nodejs.org/docs/latest/api/
15
+ import assert from 'node:assert'
16
+ import * as fs from 'node:fs/promises'
17
+ import * as path from 'node:path'
18
+
19
+ import { Logger } from '@xpack/logger'
20
+ import { ConfigurationError } from '../index.js'
21
+
22
+ // ============================================================================
23
+
24
+ /**
25
+ * Maximum recursion depth for file system operations.
26
+ *
27
+ * @remarks
28
+ * This limit protects against extremely deep directory trees that could
29
+ * cause stack overflow or performance issues. A limit of 1000 levels is
30
+ * more than sufficient for typical use cases whilst preventing pathological
31
+ * scenarios.
32
+ */
33
+ const CHMOD_RECURSIVELY_MAX_DEPTH = 42
34
+
35
+ // ============================================================================
36
+
37
+ /**
38
+ * Recursively changes file permissions within a folder tree.
39
+ *
40
+ * @remarks
41
+ * This function modifies file system permissions recursively, handling both
42
+ * files and directories with special logic to avoid permission conflicts.
43
+ *
44
+ * Processing strategy:
45
+ *
46
+ * <ol>
47
+ * <li><b>Symbolic links:</b> Ignored because links permissions
48
+ * cannot be reliably changed across platforms.</li>
49
+ * <li><b>Read-only mode:</b> Process folder contents first (recursively),
50
+ * then set
51
+ * the folder itself to read-only. This prevents permission denied errors
52
+ * when trying to access a read-only folder's contents.</li>
53
+ * <li><b>Read-write mode:</b> Set folder to read-write first, then process
54
+ * contents
55
+ * recursively. This ensures the folder is writable before attempting to
56
+ * modify nested items.</li>
57
+ * </ol>
58
+ *
59
+ * Permission modes applied:
60
+ *
61
+ * <ul>
62
+ * <li><b>Read-only:</b> Removes all write bits (user, group, other)
63
+ * using bitwise
64
+ * AND with negated <code>S_IWUSR</code> | <code>S_IWGRP</code> |
65
+ * <code>S_IWOTH</code>.</li>
66
+ * <li><b>Read-write:</b> Adds only user write bit using bitwise OR with
67
+ * <code>S_IWUSR</code>,
68
+ * preserving existing group and other permissions.</li>
69
+ * </ul>
70
+ *
71
+ * The function validates the result after each chmod operation and logs
72
+ * warnings if the expected permission state is not achieved, which can
73
+ * occur on filesystems with non-standard permission handling.
74
+ *
75
+ * Recursion depth is limited to `CHMOD_RECURSIVELY_MAX_DEPTH` levels to
76
+ * protect against extremely deep directory trees.
77
+ *
78
+ * @param inputPath - The file or folder path to process.
79
+ * @param readOnly - Whether to set permissions to read-only.
80
+ * @param log - The logger instance for output and diagnostics.
81
+ * @param depth - Internal parameter tracking recursion depth.
82
+ * @returns A promise that resolves when all permissions have been updated.
83
+ *
84
+ * @throws {@link ConfigurationError}
85
+ * If recursion depth exceeds the maximum limit.
86
+ */
87
+ export async function chmodRecursively({
88
+ inputPath,
89
+ readOnly,
90
+ log,
91
+ depth = 0,
92
+ maxDepth = CHMOD_RECURSIVELY_MAX_DEPTH,
93
+ }: {
94
+ inputPath: string
95
+ readOnly: boolean
96
+ log: Logger
97
+ depth?: number
98
+ maxDepth?: number
99
+ }): Promise<void> {
100
+ assert(inputPath, 'inputPath is required')
101
+ assert(log, 'log is required')
102
+ assert(maxDepth > 0, 'maxDepth must be a positive integer')
103
+
104
+ /* c8 ignore start - defensive guard for pathological directory trees. */
105
+ if (depth > maxDepth) {
106
+ throw new ConfigurationError(
107
+ `Recursion depth limit exceeded ` +
108
+ `(${String(maxDepth)} levels) ` +
109
+ `whilst processing: ${inputPath}`
110
+ )
111
+ }
112
+ /* c8 ignore stop */
113
+
114
+ const stat = await fs.lstat(inputPath)
115
+ // log.trace(util.inspect(stat))
116
+
117
+ if (stat.isSymbolicLink()) {
118
+ log.trace(inputPath, 'is a symbolic link, skipping')
119
+ // Since it is not possible to change the modes of links (lchmod
120
+ // was deprecated and worked on macOS anyway), don't bother
121
+ // with them.
122
+ return
123
+ }
124
+
125
+ // The order is important, process the folder before
126
+ // changing it to RO.
127
+ if (readOnly && stat.isDirectory()) {
128
+ log.trace(inputPath)
129
+ const dirents = await fs.readdir(inputPath, {
130
+ withFileTypes: true,
131
+ })
132
+ for (const dirent of dirents) {
133
+ await chmodRecursively({
134
+ inputPath: path.resolve(inputPath, dirent.name),
135
+ readOnly,
136
+ log,
137
+ depth: depth + 1,
138
+ })
139
+ }
140
+ }
141
+
142
+ const mode = stat.mode
143
+ // For RO, remove all W bits, for RW add only user.
144
+ const newMode = readOnly
145
+ ? mode &
146
+ ~(fs.constants.S_IWUSR | fs.constants.S_IWGRP | fs.constants.S_IWOTH)
147
+ : mode | fs.constants.S_IWUSR
148
+
149
+ // log.trace(
150
+ // `set ${inputPath} from ${mode.toString(8)} to ${newMode.toString(8)}`)
151
+ await fs.chmod(inputPath, newMode)
152
+
153
+ const actualStat = await fs.stat(inputPath)
154
+ // log.trace(`actual ${inputPath} is ${actualStat.mode.toString(8)}`)
155
+
156
+ // Safety net: These validations verify that the chmod operation succeeded.
157
+ // Modern file systems reliably apply permission changes, so these checks
158
+ // rarely fail. However, they detect edge cases such as:
159
+ // 1. File system permission restrictions (immutable flags, ACLs)
160
+ // 2. Race conditions where file permissions change between chmod and stat
161
+ // 3. Platform-specific behaviours with symbolic links
162
+ // 4. Network file systems with delayed or denied permission propagation
163
+ // The warnings alert developers to unexpected permission inconsistencies.
164
+ if (readOnly) {
165
+ /* c8 ignore start - safety net, normally it is set. */
166
+ if ((actualStat.mode & fs.constants.S_IWUSR) !== 0) {
167
+ log.warn(`${inputPath} not set to RO`)
168
+ }
169
+ /* c8 ignore stop */
170
+ } else {
171
+ /* c8 ignore start - safety net, normally it is not set. */
172
+ if ((actualStat.mode & fs.constants.S_IWUSR) === 0) {
173
+ log.warn(`${inputPath} not set to RW`)
174
+ }
175
+ /* c8 ignore stop */
176
+ }
177
+
178
+ // If RW, process the folder after changing it to RW.
179
+ if (!readOnly && stat.isDirectory()) {
180
+ log.trace(inputPath)
181
+ const dirents = await fs.readdir(inputPath, {
182
+ withFileTypes: true,
183
+ })
184
+ for (const dirent of dirents) {
185
+ await chmodRecursively({
186
+ inputPath: path.resolve(inputPath, dirent.name),
187
+ readOnly,
188
+ log,
189
+ depth: depth + 1,
190
+ })
191
+ }
192
+ }
193
+ }
194
+
195
+ // ----------------------------------------------------------------------------