@venizia/ignis-boot 0.2.0-8 → 0.2.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 (136) hide show
  1. package/LICENSE.md +0 -1
  2. package/README.md +104 -89
  3. package/dist/cjs/build-info/common/constants.d.ts +20 -0
  4. package/dist/cjs/build-info/common/constants.d.ts.map +1 -0
  5. package/dist/cjs/build-info/common/constants.js +41 -0
  6. package/dist/cjs/build-info/common/constants.js.map +1 -0
  7. package/dist/cjs/build-info/common/index.d.ts +3 -0
  8. package/dist/cjs/build-info/common/index.d.ts.map +1 -0
  9. package/dist/cjs/build-info/common/index.js +19 -0
  10. package/dist/cjs/build-info/common/index.js.map +1 -0
  11. package/dist/cjs/build-info/common/types.d.ts +16 -0
  12. package/dist/cjs/build-info/common/types.d.ts.map +1 -0
  13. package/dist/cjs/build-info/common/types.js +3 -0
  14. package/dist/cjs/build-info/common/types.js.map +1 -0
  15. package/dist/cjs/build-info/emitter.d.ts +12 -0
  16. package/dist/cjs/build-info/emitter.d.ts.map +1 -0
  17. package/dist/cjs/build-info/emitter.js +26 -0
  18. package/dist/cjs/build-info/emitter.js.map +1 -0
  19. package/dist/cjs/build-info/index.d.ts +10 -0
  20. package/dist/cjs/build-info/index.d.ts.map +1 -0
  21. package/dist/cjs/build-info/index.js +43 -0
  22. package/dist/cjs/build-info/index.js.map +1 -0
  23. package/dist/cjs/build-info/resolver.d.ts +25 -0
  24. package/dist/cjs/build-info/resolver.d.ts.map +1 -0
  25. package/dist/cjs/build-info/resolver.js +105 -0
  26. package/dist/cjs/build-info/resolver.js.map +1 -0
  27. package/dist/cjs/clis/artifacts.d.ts +3 -0
  28. package/dist/cjs/clis/artifacts.d.ts.map +1 -0
  29. package/dist/cjs/{cli.js → clis/artifacts.js} +12 -2
  30. package/dist/cjs/clis/artifacts.js.map +1 -0
  31. package/dist/cjs/clis/build-info.d.ts +3 -0
  32. package/dist/cjs/clis/build-info.d.ts.map +1 -0
  33. package/dist/cjs/clis/build-info.js +48 -0
  34. package/dist/cjs/clis/build-info.js.map +1 -0
  35. package/dist/cjs/common/install-quiet-logger.d.ts +2 -0
  36. package/dist/cjs/common/install-quiet-logger.d.ts.map +1 -0
  37. package/dist/cjs/common/install-quiet-logger.js +7 -0
  38. package/dist/cjs/common/install-quiet-logger.js.map +1 -0
  39. package/dist/cjs/common/quiet-logger.d.ts +18 -0
  40. package/dist/cjs/common/quiet-logger.d.ts.map +1 -0
  41. package/dist/cjs/common/quiet-logger.js +46 -0
  42. package/dist/cjs/common/quiet-logger.js.map +1 -0
  43. package/dist/cjs/generator/common/constants.d.ts +8 -1
  44. package/dist/cjs/generator/common/constants.d.ts.map +1 -1
  45. package/dist/cjs/generator/common/constants.js +12 -1
  46. package/dist/cjs/generator/common/constants.js.map +1 -1
  47. package/dist/cjs/generator/common/types.d.ts +5 -0
  48. package/dist/cjs/generator/common/types.d.ts.map +1 -1
  49. package/dist/cjs/generator/emitter.d.ts +14 -1
  50. package/dist/cjs/generator/emitter.d.ts.map +1 -1
  51. package/dist/cjs/generator/emitter.js +15 -3
  52. package/dist/cjs/generator/emitter.js.map +1 -1
  53. package/dist/cjs/generator/index.d.ts +4 -4
  54. package/dist/cjs/generator/index.d.ts.map +1 -1
  55. package/dist/cjs/generator/index.js +16 -9
  56. package/dist/cjs/generator/index.js.map +1 -1
  57. package/dist/cjs/generator/scanner.d.ts +8 -2
  58. package/dist/cjs/generator/scanner.d.ts.map +1 -1
  59. package/dist/cjs/generator/scanner.js +42 -14
  60. package/dist/cjs/generator/scanner.js.map +1 -1
  61. package/dist/cjs/index.d.ts +1 -0
  62. package/dist/cjs/index.d.ts.map +1 -1
  63. package/dist/cjs/index.js +1 -0
  64. package/dist/cjs/index.js.map +1 -1
  65. package/dist/esm/build-info/common/constants.d.ts +20 -0
  66. package/dist/esm/build-info/common/constants.d.ts.map +1 -0
  67. package/dist/esm/build-info/common/constants.js +36 -0
  68. package/dist/esm/build-info/common/constants.js.map +1 -0
  69. package/dist/esm/build-info/common/index.d.ts +3 -0
  70. package/dist/esm/build-info/common/index.d.ts.map +1 -0
  71. package/dist/esm/build-info/common/index.js +3 -0
  72. package/dist/esm/build-info/common/index.js.map +1 -0
  73. package/dist/esm/build-info/common/types.d.ts +16 -0
  74. package/dist/esm/build-info/common/types.d.ts.map +1 -0
  75. package/dist/esm/build-info/common/types.js +2 -0
  76. package/dist/esm/build-info/common/types.js.map +1 -0
  77. package/dist/esm/build-info/emitter.d.ts +12 -0
  78. package/dist/esm/build-info/emitter.d.ts.map +1 -0
  79. package/dist/esm/build-info/emitter.js +22 -0
  80. package/dist/esm/build-info/emitter.js.map +1 -0
  81. package/dist/esm/build-info/index.d.ts +10 -0
  82. package/dist/esm/build-info/index.d.ts.map +1 -0
  83. package/dist/esm/build-info/index.js +25 -0
  84. package/dist/esm/build-info/index.js.map +1 -0
  85. package/dist/esm/build-info/resolver.d.ts +25 -0
  86. package/dist/esm/build-info/resolver.d.ts.map +1 -0
  87. package/dist/esm/build-info/resolver.js +101 -0
  88. package/dist/esm/build-info/resolver.js.map +1 -0
  89. package/dist/esm/clis/artifacts.d.ts +3 -0
  90. package/dist/esm/clis/artifacts.d.ts.map +1 -0
  91. package/dist/esm/{cli.js → clis/artifacts.js} +12 -2
  92. package/dist/esm/clis/artifacts.js.map +1 -0
  93. package/dist/esm/clis/build-info.d.ts +3 -0
  94. package/dist/esm/clis/build-info.d.ts.map +1 -0
  95. package/dist/esm/clis/build-info.js +46 -0
  96. package/dist/esm/clis/build-info.js.map +1 -0
  97. package/dist/esm/common/install-quiet-logger.d.ts +2 -0
  98. package/dist/esm/common/install-quiet-logger.d.ts.map +1 -0
  99. package/dist/esm/common/install-quiet-logger.js +5 -0
  100. package/dist/esm/common/install-quiet-logger.js.map +1 -0
  101. package/dist/esm/common/quiet-logger.d.ts +18 -0
  102. package/dist/esm/common/quiet-logger.d.ts.map +1 -0
  103. package/dist/esm/common/quiet-logger.js +42 -0
  104. package/dist/esm/common/quiet-logger.js.map +1 -0
  105. package/dist/esm/generator/common/constants.d.ts +8 -1
  106. package/dist/esm/generator/common/constants.d.ts.map +1 -1
  107. package/dist/esm/generator/common/constants.js +12 -1
  108. package/dist/esm/generator/common/constants.js.map +1 -1
  109. package/dist/esm/generator/common/index.d.ts +2 -2
  110. package/dist/esm/generator/common/index.js +2 -2
  111. package/dist/esm/generator/common/types.d.ts +6 -1
  112. package/dist/esm/generator/common/types.d.ts.map +1 -1
  113. package/dist/esm/generator/emitter.d.ts +15 -2
  114. package/dist/esm/generator/emitter.d.ts.map +1 -1
  115. package/dist/esm/generator/emitter.js +16 -4
  116. package/dist/esm/generator/emitter.js.map +1 -1
  117. package/dist/esm/generator/index.d.ts +7 -7
  118. package/dist/esm/generator/index.d.ts.map +1 -1
  119. package/dist/esm/generator/index.js +21 -14
  120. package/dist/esm/generator/index.js.map +1 -1
  121. package/dist/esm/generator/scanner.d.ts +8 -2
  122. package/dist/esm/generator/scanner.d.ts.map +1 -1
  123. package/dist/esm/generator/scanner.js +43 -15
  124. package/dist/esm/generator/scanner.js.map +1 -1
  125. package/dist/esm/index.d.ts +2 -1
  126. package/dist/esm/index.d.ts.map +1 -1
  127. package/dist/esm/index.js +2 -1
  128. package/dist/esm/index.js.map +1 -1
  129. package/dist/esm/package.json +4 -0
  130. package/package.json +15 -8
  131. package/dist/cjs/cli.d.ts +0 -3
  132. package/dist/cjs/cli.d.ts.map +0 -1
  133. package/dist/cjs/cli.js.map +0 -1
  134. package/dist/esm/cli.d.ts +0 -3
  135. package/dist/esm/cli.d.ts.map +0 -1
  136. package/dist/esm/cli.js.map +0 -1
package/LICENSE.md CHANGED
@@ -47,7 +47,6 @@ This package uses the following open-source libraries:
47
47
  | [@venizia/ignis-helpers](https://github.com/VENIZIA-AI/ignis) | MIT | Ignis utility helpers |
48
48
  | [@venizia/ignis-inversion](https://github.com/VENIZIA-AI/ignis) | MIT | Ignis DI/IoC container |
49
49
  | [glob](https://github.com/isaacs/node-glob) | ISC | File pattern matching |
50
- | [lodash](https://github.com/lodash/lodash) | MIT | Utility library |
51
50
  | [reflect-metadata](https://github.com/rbuckton/reflect-metadata) | Apache-2.0 | Metadata reflection API |
52
51
  | [zod](https://github.com/colinhacks/zod) | MIT | TypeScript-first schema validation |
53
52
 
package/README.md CHANGED
@@ -1,56 +1,31 @@
1
- <div align="center">
1
+ # @venizia/ignis-boot
2
2
 
3
- <br />
4
-
5
- # :fire: IGNIS - `@venizia/ignis-boot`
6
-
7
- **Generates one static registration file from your decorated classes.**
8
-
9
- [![Docs](https://img.shields.io/badge/Docs-ignis.venizia.ai-2563EB.svg?style=flat-square)](https://ignis.venizia.ai/references/base/bootstrapping)
10
- [![npm](https://img.shields.io/npm/v/@venizia/ignis-boot.svg?style=flat-square&color=cb3837&label=@venizia/ignis-boot)](https://www.npmjs.com/package/@venizia/ignis-boot)
11
- [![License: MIT](https://img.shields.io/badge/License-MIT-3DA639.svg?style=flat-square)](LICENSE.md)
12
- [![TypeScript](https://img.shields.io/badge/TypeScript-6.x-3178C6.svg?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
13
-
14
- [Artifact registration reference](https://ignis.venizia.ai/references/base/bootstrapping) &#8226;
15
- [Registering artifacts guide](https://ignis.venizia.ai/guides/core-concepts/application/bootstrapping) &#8226;
16
- [Changelog](https://ignis.venizia.ai/changelogs/2026-09-03-deprecated-boot-api-removed)
17
-
18
- </div>
19
-
20
- ---
21
-
22
- `ignis-artifacts` walks a source root with the TypeScript compiler API, finds every named,
23
- non-abstract class that carries an IGNIS stereotype decorator (`@service`, `@controller`, ...), and
24
- writes one file of plain static imports: `src/generated/artifacts.ts`. The application passes that
25
- file as `configs.artifacts`; `@venizia/ignis`'s `registerArtifacts` boot step binds it.
26
-
27
- **This package runs at build time only.** No decorator runs and no class is imported while
28
- scanning - detection reads source text, so a datasource that opens a pool at import stays inert.
3
+ Build-time tools for an IGNIS application: `ignis-artifacts` writes one static file that registers your decorated classes, and `ignis-build-info` stamps the build. Install it as a devDependency of the application.
29
4
 
30
5
  ## Install
31
6
 
32
7
  ```bash
33
- bun add -d @venizia/ignis-boot
8
+ bun add -d @venizia/ignis-boot typescript
34
9
  ```
35
10
 
36
- `ignis-artifacts` runs at build time; the generated file imports nothing from this package, so a
37
- deployed application installs nothing extra for it at runtime.
11
+ `typescript` 5 or 6 is a required peer: the scanner reads your source with the TypeScript compiler API. Both binaries run under Bun (they carry a `bun` shebang).
38
12
 
39
- ## Use it
13
+ ## Register your classes
40
14
 
41
15
  Decorate the classes you want registered:
42
16
 
43
17
  ```typescript
44
- import { component, service } from '@venizia/ignis';
18
+ import { BaseService, service } from '@venizia/ignis';
45
19
 
46
20
  @service()
47
- export class PricingService extends BaseService {}
48
-
49
- @component()
50
- export class MetricsComponent extends BaseComponent {}
21
+ export class PricingService extends BaseService {
22
+ constructor() {
23
+ super({ scope: PricingService.name });
24
+ }
25
+ }
51
26
  ```
52
27
 
53
- Add the generator scripts and run one:
28
+ Add two scripts and run the first one:
54
29
 
55
30
  ```json
56
31
  {
@@ -61,13 +36,26 @@ Add the generator scripts and run one:
61
36
  }
62
37
  ```
63
38
 
64
- ```bash
65
- bun run generate:artifacts
39
+ `generate` writes plain static imports and one object:
40
+
41
+ ```typescript
42
+ // AUTO-GENERATED by @venizia/ignis-boot - do not edit. Regenerate: ignis-artifacts generate --root src --out src/generated/artifacts.ts
43
+ import { PricingService } from '../services/pricing.service';
44
+
45
+ export const GeneratedArtifacts = {
46
+ configurations: [],
47
+ dataSources: [],
48
+ components: [],
49
+ repositories: [],
50
+ services: [PricingService],
51
+ controllers: [],
52
+ };
66
53
  ```
67
54
 
68
- Pass the generated index to your application:
55
+ Hand that object to the application. The `registerArtifacts` boot step binds every class in it:
69
56
 
70
57
  ```typescript
58
+ import type { IApplicationConfigs } from '@venizia/ignis';
71
59
  import { GeneratedArtifacts } from './generated/artifacts';
72
60
 
73
61
  export const configs: IApplicationConfigs = {
@@ -76,57 +64,91 @@ export const configs: IApplicationConfigs = {
76
64
  };
77
65
  ```
78
66
 
79
- Commit the generated file; never edit it by hand - the next `generate` overwrites it. Full
80
- walkthrough: [Registering artifacts](https://ignis.venizia.ai/guides/core-concepts/application/bootstrapping).
67
+ Commit the generated file and never edit it by hand. Run `check:artifacts` where lint runs, so a stale index fails before it reaches CI.
81
68
 
82
- ## CLI
69
+ The scanner reads source text and never imports a module. No decorator runs and no import side effect fires, so a datasource that opens a pool at import stays inert. Because the output is plain imports, `bun build --compile` sees every class; a compiled binary has no source tree to scan at run time.
83
70
 
84
- Shipped as the `ignis-artifacts` binary. Requires `typescript >= 5` (peer dependency) and runs
85
- under bun.
71
+ ## `ignis-artifacts`
86
72
 
87
73
  ```
88
74
  ignis-artifacts <generate|check> [--root src] [--out src/generated/artifacts.ts] [--ignore a,b] [--export GeneratedArtifacts]
89
75
  ```
90
76
 
91
77
  | Flag | Default | Meaning |
92
- | :--- | :--- | :--- |
93
- | `--root` | `src` | Directory to scan, recursively |
94
- | `--out` | `src/generated/artifacts.ts` | Output path; import paths are relative to it |
95
- | `--ignore` | `**/__tests__/**`, `**/*.test.ts`, `**/*.spec.ts`, `**/generated/**` | Comma-separated globs, merged with the default ignore list - never replaces it |
78
+ |---|---|---|
79
+ | `--root` | `src` | Directory to scan, recursively (`.ts` files, `.d.ts` skipped) |
80
+ | `--out` | `src/generated/artifacts.ts` | Output file; import paths are relative to it |
81
+ | `--ignore` | none | Comma-separated globs, relative to `--root`, added to the default list |
96
82
  | `--export` | `GeneratedArtifacts` | Name of the exported constant |
97
83
 
84
+ The default ignore list always applies: `**/__tests__/**`, `**/*.test.ts`, `**/*.spec.ts`, `**/generated/**`. A decorated class hidden by one of your own `--ignore` patterns prints one `warning:` line on stderr.
85
+
98
86
  | Command | Effect | Exit code |
99
- | :--- | :--- | :--- |
100
- | `generate` | Writes `--out` when its content changed | `0` |
101
- | `check` | Renders in memory and compares with `--out`; fails a stale index | `0` fresh, `1` stale |
87
+ |---|---|---|
88
+ | `generate` | Writes `--out` only when the content changed | `0` |
89
+ | `check` | Renders in memory and compares with `--out`, ignoring the header line | `0` fresh, `1` stale or missing |
90
+ | anything else | Prints the usage line | `2` |
102
91
 
103
- Default ignore list: `**/__tests__/**`, `**/*.test.ts`, `**/*.spec.ts`, `**/generated/**`.
92
+ ### What gets emitted
104
93
 
105
- ## Detection rules
94
+ A class lands in the index when all of these hold. The scanner logs a warning for a decorated class it skips.
106
95
 
107
- A class is emitted when every rule holds. Every miss is logged with its reason.
96
+ - It is a named export (`export class` or `export { Name }`), not `export default`.
97
+ - It is not `abstract`.
98
+ - It carries exactly one of `@configuration`, `@component`, `@controller`, `@service`, `@repository` or `@datasource`, imported by name from `@venizia/ignis` or `@venizia/ignis-kernel` (aliases resolved). `@injectable({ type })` counts when `type` is a string literal or `ArtifactTypes.<NAME>`.
108
99
 
109
- - Named export (`export class`), not `export default`, not module-private.
110
- - Not `abstract`.
111
- - Decorated with `component`, `controller`, `service`, `repository` or `datasource`, imported from
112
- `@venizia/ignis` or `@venizia/ignis-kernel` (import aliases resolved) - or with
113
- `injectable({ type })` where `type` is a string literal or `ArtifactTypes.<NAME>`.
100
+ `@model` classes are recognized and never emitted - a repository registers its model. A decorator re-exported through your own wrapper module is not recognized. Two stereotypes on one class fail the scan.
101
+
102
+ The output is deterministic: imports sorted by path, names sorted within each field, empty fields kept, a field wider than 100 columns wrapped one name per line.
103
+
104
+ ## `ignis-build-info`
105
+
106
+ A compiled binary or a Distroless image has no `.git` and no readable `package.json`, so `ignis-build-info` resolves the build stamp at build time and writes it as a static file.
107
+
108
+ ```
109
+ ignis-build-info generate [--out src/_build_info.ts] [--root .] [--format ts|json] [--export BUILD_INFO]
110
+ ```
111
+
112
+ | Flag | Default | Meaning |
113
+ |---|---|---|
114
+ | `--out` | `src/_build_info.ts` | Output file; rewritten on every run |
115
+ | `--root` | `.` | Where `package.json` is read and `git` runs |
116
+ | `--format` | `ts` | `ts` writes an `export const`; `json` writes the same record as JSON |
117
+ | `--export` | `BUILD_INFO` | Name of the exported constant in `ts` format |
118
+
119
+ Each field reads the environment first (first non-blank key wins), then its fallback. A field nothing resolves reads `unspecified`.
120
+
121
+ | Field | Environment keys, in order | Fallback |
122
+ |---|---|---|
123
+ | `service` | - | `package.json` `name` |
124
+ | `version` | `APP_ENV_BUILD_VERSION`, `APP_BUILD_VERSION`, `CI_COMMIT_TAG`, `GITHUB_REF_NAME` | `package.json` `version` |
125
+ | `commit` | `APP_ENV_BUILD_COMMIT_TAG`, `APP_BUILD_COMMIT`, `CI_COMMIT_SHA`, `GITHUB_SHA`, `COMMIT_SHA` | `git rev-parse --short=12 HEAD` |
126
+ | `branch` | `APP_BUILD_BRANCH`, `CI_COMMIT_REF_NAME`, `GITHUB_REF_NAME`, `BRANCH_NAME` | `git rev-parse --abbrev-ref HEAD` |
127
+ | `builtAt` | `APP_ENV_BUILD_DATE`, `APP_BUILD_DATE` | the time the generator ran |
128
+
129
+ Register the stamp once at the entry point. The health-check component's `/stats` route reports it:
130
+
131
+ ```typescript
132
+ import { BuildInfoRegistry } from '@venizia/ignis-helpers/core';
133
+ import { BUILD_INFO } from './_build_info';
134
+
135
+ BuildInfoRegistry.set({ buildInfo: BUILD_INFO });
136
+ ```
114
137
 
115
- `model` classes are recognised and never emitted - a model is reached through its repository.
138
+ ## Entry points
116
139
 
117
- ## Programmatic API
140
+ | Subpath | What it gives | Extra peers |
141
+ |---|---|---|
142
+ | `@venizia/ignis-boot` | Everything from the two subpaths below | `typescript` |
143
+ | `@venizia/ignis-boot/generator` | `generateArtifactIndex`, `checkArtifactIndex`, `ArtifactScanner`, `ArtifactIndexEmitter`, `ArtifactStereotypes`, `ArtifactTypes`, `ArtifactIndexFields` | `typescript` |
144
+ | `@venizia/ignis-boot/build-info` | `generateBuildInfo`, `BuildInfoResolver`, `BuildInfoEmitter`, `BuildInfoFormats`, `BuildInfoEnvironmentKeys` | none |
118
145
 
119
- `@venizia/ignis-boot/generator` exports the same machinery as functions, for a caller that is not
120
- a shell script.
146
+ | Binary | File |
147
+ |---|---|
148
+ | `ignis-artifacts` | `dist/cjs/clis/artifacts.js` |
149
+ | `ignis-build-info` | `dist/cjs/clis/build-info.js` |
121
150
 
122
- | Export | What it is |
123
- | :--- | :--- |
124
- | `generateArtifactIndex(opts)` | Renders and writes `opts.out` when the content changed |
125
- | `checkArtifactIndex(opts)` | Renders in memory and compares with `opts.out`, without writing |
126
- | `IGenerateOptions` | `{ root, out, ignore?, exportName? }` - shared options for both |
127
- | `ArtifactScanner.getInstance().scan({ root, ignore? })` | The scan step alone, as `IScannedArtifact[]` |
128
- | `ArtifactIndexEmitter.render({ artifacts, outFile, exportName })` | The render step alone, as text |
129
- | `ArtifactStereotypes`, `ArtifactTypes`, `ArtifactIndexFields` | The decorator-to-type map and the emitted field names |
151
+ The package ships CommonJS and ESM builds. The programmatic API mirrors the CLI:
130
152
 
131
153
  ```typescript
132
154
  import { checkArtifactIndex } from '@venizia/ignis-boot/generator';
@@ -134,26 +156,19 @@ import { checkArtifactIndex } from '@venizia/ignis-boot/generator';
134
156
  const { isFresh } = checkArtifactIndex({ root: 'src', out: 'src/generated/artifacts.ts' });
135
157
  ```
136
158
 
137
- ## What you must know
159
+ ## Where it sits
138
160
 
139
- - **Output is deterministic.** Imports sorted by path, class names sorted within each field, one
140
- field per kind, empty arrays kept. A field wider than 100 columns wraps one name per line, so the
141
- file passes the repo's `prettier -l` unchanged.
142
- - **The scanner never executes a module.** No decorator runs and no import side effect fires while
143
- scanning, so a stereotype re-exported through a local wrapper module is not recognised - the
144
- import must name an IGNIS module directly.
145
- - **`check` belongs where lint runs**, not only in CI - a stale index should fail locally before a
146
- push reaches the pipeline.
161
+ `dev-configs -> inversion -> {filter, helpers} -> {boot, kernel} -> connectors -> {core-worker, core-server} -> atlas`. Boot depends on `@venizia/ignis-helpers` only; no IGNIS package depends on boot, so the application declares it.
147
162
 
148
- Full detail: [Artifact registration reference](https://ignis.venizia.ai/references/base/bootstrapping).
163
+ The generated index replaces the runtime booters IGNIS once took from LoopBack 4, which globbed the file system at startup.
149
164
 
150
165
  ## Links
151
166
 
152
- [Documentation](https://ignis.venizia.ai) &#8226;
153
- [Quickstart](https://ignis.venizia.ai/guides/get-started/5-minute-quickstart) &#8226;
154
- [Core API](https://ignis.venizia.ai/references/) &#8226;
155
- [Best practices](https://ignis.venizia.ai/best-practices/) &#8226;
156
- [Changelog](https://ignis.venizia.ai/changelogs/)
167
+ - [Registering artifacts (guide)](https://ignis.venizia.ai/guides/core-concepts/application/bootstrapping)
168
+ - [Artifact registration (reference)](https://ignis.venizia.ai/references/base/bootstrapping)
169
+ - [Build info](https://ignis.venizia.ai/guides/core-concepts/application/build-info)
170
+ - [Runtime boot API removed](https://ignis.venizia.ai/changelogs/2026-09-03-deprecated-boot-api-removed)
171
+ - [Changelogs](https://ignis.venizia.ai/changelogs/)
172
+ - [Source](https://github.com/VENIZIA-AI/ignis/blob/main/packages/boot)
157
173
 
158
- MIT licensed - see [LICENSE.md](LICENSE.md).
159
- Questions: [GitHub Issues](https://github.com/VENIZIA-AI/ignis/issues) &#8226; developer@venizia.ai
174
+ MIT licensed - see [LICENSE.md](./LICENSE.md).
@@ -0,0 +1,20 @@
1
+ import type { TConstValue } from '@venizia/ignis-helpers';
2
+ export declare class BuildInfoFormats {
3
+ static readonly TS = "ts";
4
+ static readonly JSON = "json";
5
+ static readonly SCHEME_SET: Set<string>;
6
+ static isValid(value: string): boolean;
7
+ }
8
+ export type TBuildInfoFormat = TConstValue<typeof BuildInfoFormats>;
9
+ /**
10
+ * Each field is spelled differently by every CI provider, and `APP_ENV_*` is what this
11
+ * organisation's pipelines export - so it is read before the generic `APP_BUILD_*` pair and before
12
+ * anything provider-specific. The first key carrying a non-blank value wins.
13
+ */
14
+ export declare class BuildInfoEnvironmentKeys {
15
+ static readonly VERSION: readonly string[];
16
+ static readonly COMMIT: readonly string[];
17
+ static readonly BRANCH: readonly string[];
18
+ static readonly BUILT_AT: readonly string[];
19
+ }
20
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/build-info/common/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,qBAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,EAAE,QAAQ;IAC1B,MAAM,CAAC,QAAQ,CAAC,IAAI,UAAU;IAE9B,MAAM,CAAC,QAAQ,CAAC,UAAU,cAAyC;IAEnE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAGvC;AAED,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEpE;;;;GAIG;AACH,qBAAa,wBAAwB;IACnC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAKxC;IAEF,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAMvC;IAEF,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAKvC;IAEF,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAA4C;CACxF"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BuildInfoEnvironmentKeys = exports.BuildInfoFormats = void 0;
4
+ class BuildInfoFormats {
5
+ static { this.TS = 'ts'; }
6
+ static { this.JSON = 'json'; }
7
+ static { this.SCHEME_SET = new Set([this.TS, this.JSON]); }
8
+ static isValid(value) {
9
+ return this.SCHEME_SET.has(value);
10
+ }
11
+ }
12
+ exports.BuildInfoFormats = BuildInfoFormats;
13
+ /**
14
+ * Each field is spelled differently by every CI provider, and `APP_ENV_*` is what this
15
+ * organisation's pipelines export - so it is read before the generic `APP_BUILD_*` pair and before
16
+ * anything provider-specific. The first key carrying a non-blank value wins.
17
+ */
18
+ class BuildInfoEnvironmentKeys {
19
+ static { this.VERSION = [
20
+ 'APP_ENV_BUILD_VERSION',
21
+ 'APP_BUILD_VERSION',
22
+ 'CI_COMMIT_TAG',
23
+ 'GITHUB_REF_NAME',
24
+ ]; }
25
+ static { this.COMMIT = [
26
+ 'APP_ENV_BUILD_COMMIT_TAG',
27
+ 'APP_BUILD_COMMIT',
28
+ 'CI_COMMIT_SHA',
29
+ 'GITHUB_SHA',
30
+ 'COMMIT_SHA',
31
+ ]; }
32
+ static { this.BRANCH = [
33
+ 'APP_BUILD_BRANCH',
34
+ 'CI_COMMIT_REF_NAME',
35
+ 'GITHUB_REF_NAME',
36
+ 'BRANCH_NAME',
37
+ ]; }
38
+ static { this.BUILT_AT = ['APP_ENV_BUILD_DATE', 'APP_BUILD_DATE']; }
39
+ }
40
+ exports.BuildInfoEnvironmentKeys = BuildInfoEnvironmentKeys;
41
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/build-info/common/constants.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;aACX,OAAE,GAAG,IAAI,CAAC;aACV,SAAI,GAAG,MAAM,CAAC;aAEd,eAAU,GAAG,IAAI,GAAG,CAAS,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAEnE,MAAM,CAAC,OAAO,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;;AARH,4CASC;AAID;;;;GAIG;AACH,MAAa,wBAAwB;aACnB,YAAO,GAAsB;QAC3C,uBAAuB;QACvB,mBAAmB;QACnB,eAAe;QACf,iBAAiB;KAClB,CAAC;aAEc,WAAM,GAAsB;QAC1C,0BAA0B;QAC1B,kBAAkB;QAClB,eAAe;QACf,YAAY;QACZ,YAAY;KACb,CAAC;aAEc,WAAM,GAAsB;QAC1C,kBAAkB;QAClB,oBAAoB;QACpB,iBAAiB;QACjB,aAAa;KACd,CAAC;aAEc,aAAQ,GAAsB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;;AAvBzF,4DAwBC"}
@@ -0,0 +1,3 @@
1
+ export * from './constants';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/build-info/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./constants"), exports);
18
+ __exportStar(require("./types"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/build-info/common/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,0CAAwB"}
@@ -0,0 +1,16 @@
1
+ import type { TBuildInfoRecord } from '@venizia/ignis-helpers/core';
2
+ import type { TBuildInfoFormat } from './constants';
3
+ export interface IBuildInfoOptions {
4
+ out: string;
5
+ /** Where `package.json` is read from and where `git` is asked; default the cwd. */
6
+ root?: string;
7
+ format?: TBuildInfoFormat;
8
+ /** Name of the exported constant in `ts` format; default `BUILD_INFO`. */
9
+ exportName?: string;
10
+ }
11
+ export interface IBuildInfoResult {
12
+ buildInfo: TBuildInfoRecord;
13
+ content: string;
14
+ out: string;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/build-info/common/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/build-info/common/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { TBuildInfoRecord } from '@venizia/ignis-helpers/core';
2
+ import { type TBuildInfoFormat } from './common';
3
+ /** Renders the stamp: a static `ts` const a bundler bakes in, or the same record as `json` for a Vite app or a Tauri shell. */
4
+ export declare class BuildInfoEmitter {
5
+ static readonly DEFAULT_EXPORT_NAME = "BUILD_INFO";
6
+ static render(opts: {
7
+ buildInfo: TBuildInfoRecord;
8
+ format: TBuildInfoFormat;
9
+ exportName: string;
10
+ }): string;
11
+ }
12
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../../src/build-info/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEnE,+HAA+H;AAC/H,qBAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,mBAAmB,gBAAgB;IAEnD,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAClB,SAAS,EAAE,gBAAgB,CAAC;QAC5B,MAAM,EAAE,gBAAgB,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,MAAM;CAmBX"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BuildInfoEmitter = void 0;
4
+ const common_1 = require("./common");
5
+ /** Renders the stamp: a static `ts` const a bundler bakes in, or the same record as `json` for a Vite app or a Tauri shell. */
6
+ class BuildInfoEmitter {
7
+ static { this.DEFAULT_EXPORT_NAME = 'BUILD_INFO'; }
8
+ static render(opts) {
9
+ const { buildInfo, format, exportName } = opts;
10
+ if (format === common_1.BuildInfoFormats.JSON) {
11
+ return `${JSON.stringify(buildInfo, null, 2)}\n`;
12
+ }
13
+ const fields = Object.entries(buildInfo)
14
+ .map(([key, value]) => ` ${key}: ${JSON.stringify(value)},`)
15
+ .join('\n');
16
+ return [
17
+ '// AUTO-GENERATED by @venizia/ignis-boot - do not edit. Regenerate: ignis-build-info generate',
18
+ `export const ${exportName} = {`,
19
+ fields,
20
+ '} as const;',
21
+ '',
22
+ ].join('\n');
23
+ }
24
+ }
25
+ exports.BuildInfoEmitter = BuildInfoEmitter;
26
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../../src/build-info/emitter.ts"],"names":[],"mappings":";;;AACA,qCAAmE;AAEnE,+HAA+H;AAC/H,MAAa,gBAAgB;aACX,wBAAmB,GAAG,YAAY,CAAC;IAEnD,MAAM,CAAC,MAAM,CAAC,IAIb;QACC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;QAE/C,IAAI,MAAM,KAAK,yBAAgB,CAAC,IAAI,EAAE,CAAC;YACrC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;QACnD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;aACrC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;aAC5D,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,OAAO;YACL,+FAA+F;YAC/F,gBAAgB,UAAU,MAAM;YAChC,MAAM;YACN,aAAa;YACb,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;;AAzBH,4CA0BC"}
@@ -0,0 +1,10 @@
1
+ import { type IBuildInfoOptions, type IBuildInfoResult } from './common';
2
+ /**
3
+ * Writes the stamp. Unlike the artifact index this ALWAYS writes: `builtAt` changes every run, so
4
+ * an unchanged-content short-circuit would be dead code that hides a stale timestamp.
5
+ */
6
+ export declare const generateBuildInfo: (opts: IBuildInfoOptions) => Promise<IBuildInfoResult>;
7
+ export * from './common';
8
+ export * from './emitter';
9
+ export * from './resolver';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/build-info/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAI3F;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,MAAM,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAazF,CAAC;AAEF,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.generateBuildInfo = void 0;
18
+ const node_fs_1 = require("node:fs");
19
+ const node_path_1 = require("node:path");
20
+ const common_1 = require("./common");
21
+ const emitter_1 = require("./emitter");
22
+ const resolver_1 = require("./resolver");
23
+ /**
24
+ * Writes the stamp. Unlike the artifact index this ALWAYS writes: `builtAt` changes every run, so
25
+ * an unchanged-content short-circuit would be dead code that hides a stale timestamp.
26
+ */
27
+ const generateBuildInfo = async (opts) => {
28
+ const buildInfo = await resolver_1.BuildInfoResolver.getInstance().resolve({ root: opts.root });
29
+ const content = emitter_1.BuildInfoEmitter.render({
30
+ buildInfo,
31
+ format: opts.format ?? common_1.BuildInfoFormats.TS,
32
+ exportName: opts.exportName ?? emitter_1.BuildInfoEmitter.DEFAULT_EXPORT_NAME,
33
+ });
34
+ const out = (0, node_path_1.resolve)(opts.out);
35
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(out), { recursive: true });
36
+ (0, node_fs_1.writeFileSync)(out, content);
37
+ return { buildInfo, content, out };
38
+ };
39
+ exports.generateBuildInfo = generateBuildInfo;
40
+ __exportStar(require("./common"), exports);
41
+ __exportStar(require("./emitter"), exports);
42
+ __exportStar(require("./resolver"), exports);
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/build-info/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,qCAAmD;AACnD,yCAA6C;AAC7C,qCAA2F;AAC3F,uCAA6C;AAC7C,yCAA+C;AAE/C;;;GAGG;AACI,MAAM,iBAAiB,GAAG,KAAK,EAAE,IAAuB,EAA6B,EAAE;IAC5F,MAAM,SAAS,GAAG,MAAM,4BAAiB,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrF,MAAM,OAAO,GAAG,0BAAgB,CAAC,MAAM,CAAC;QACtC,SAAS;QACT,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,yBAAgB,CAAC,EAAE;QAC1C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,0BAAgB,CAAC,mBAAmB;KACpE,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,IAAA,mBAAO,EAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAA,mBAAS,EAAC,IAAA,mBAAO,EAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,IAAA,uBAAa,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAE5B,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AACrC,CAAC,CAAC;AAbW,QAAA,iBAAiB,qBAa5B;AAEF,2CAAyB;AACzB,4CAA0B;AAC1B,6CAA2B"}
@@ -0,0 +1,25 @@
1
+ import { BaseHelper } from '@venizia/ignis-helpers';
2
+ import { type TBuildInfoRecord } from '@venizia/ignis-helpers/core';
3
+ /**
4
+ * Resolves the stamp: environment first (CI knows the release tag), then `git`, then the
5
+ * manifest. Every miss is logged with its reason and degrades one field to
6
+ * {@link BuildInfoRegistry.UNSPECIFIED} - a Distroless image has no `git` and an exported tarball
7
+ * has no manifest, and a build must not die over a stamp.
8
+ */
9
+ export declare class BuildInfoResolver extends BaseHelper {
10
+ private static instance?;
11
+ private constructor();
12
+ static getInstance(): BuildInfoResolver;
13
+ resolve(opts?: {
14
+ root?: string;
15
+ }): Promise<TBuildInfoRecord>;
16
+ private resolveEnvironmentValue;
17
+ /**
18
+ * `nothrow()` covers a nonzero exit; the `catch` covers `git` being absent and also `Bun` being
19
+ * absent - under plain Node the `ReferenceError` degrades the field instead of killing the build.
20
+ */
21
+ private resolveGitValue;
22
+ /** `JSON.parse` promises nothing about the shape, so a manifest holding `{ "name": 42 }` must not become a numeric `service`. */
23
+ private resolveManifest;
24
+ }
25
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/build-info/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAQvF;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAoB;IAE5C,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,iBAAiB;IAIjC,OAAO,CAAC,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkClE,OAAO,CAAC,uBAAuB;IAW/B;;;OAGG;YACW,eAAe;IA4B7B,iIAAiI;IACjI,OAAO,CAAC,eAAe;CAyBxB"}