@enspirit/emb 0.0.1 → 0.0.3

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 (164) hide show
  1. package/README.md +190 -164
  2. package/bin/run.js +8 -2
  3. package/dist/src/cli/abstract/FlavouredCommand.d.ts +12 -0
  4. package/dist/src/cli/abstract/FlavouredCommand.js +40 -0
  5. package/dist/src/cli/abstract/index.d.ts +1 -0
  6. package/dist/src/cli/abstract/index.js +1 -0
  7. package/dist/src/cli/commands/clean.d.ts +15 -0
  8. package/dist/src/cli/commands/clean.js +29 -0
  9. package/dist/src/cli/commands/components/build.d.ts +11 -0
  10. package/dist/src/cli/commands/components/build.js +24 -0
  11. package/dist/src/cli/commands/components/index.d.ts +14 -0
  12. package/dist/src/cli/commands/components/index.js +44 -0
  13. package/dist/src/cli/commands/config/print.d.ts +13 -0
  14. package/dist/src/cli/commands/config/print.js +16 -0
  15. package/dist/src/cli/commands/containers/index.d.ts +12 -0
  16. package/dist/src/cli/commands/containers/index.js +66 -0
  17. package/dist/src/cli/commands/containers/prune.d.ts +8 -0
  18. package/dist/src/cli/commands/containers/prune.js +23 -0
  19. package/dist/src/cli/commands/down.d.ts +8 -0
  20. package/dist/src/cli/commands/down.js +42 -0
  21. package/dist/src/cli/commands/images/delete.d.ts +10 -0
  22. package/dist/src/cli/commands/images/delete.js +44 -0
  23. package/dist/src/cli/commands/images/index.d.ts +17 -0
  24. package/dist/src/cli/commands/images/index.js +59 -0
  25. package/dist/src/cli/commands/images/prune.d.ts +11 -0
  26. package/dist/src/cli/commands/images/prune.js +35 -0
  27. package/dist/src/cli/commands/run/index.d.ts +10 -0
  28. package/dist/src/cli/commands/run/index.js +49 -0
  29. package/dist/src/cli/commands/tasks/index.d.ts +9 -0
  30. package/dist/src/cli/commands/tasks/index.js +23 -0
  31. package/dist/src/cli/commands/tasks/run.d.ts +16 -0
  32. package/dist/src/cli/commands/tasks/run.js +113 -0
  33. package/dist/src/cli/commands/up.d.ts +10 -0
  34. package/dist/src/cli/commands/up.js +49 -0
  35. package/dist/src/cli/constants.d.ts +2 -0
  36. package/dist/src/cli/constants.js +6 -0
  37. package/dist/src/cli/hooks/init.d.ts +3 -0
  38. package/dist/src/cli/hooks/init.js +27 -0
  39. package/dist/src/cli/index.d.ts +3 -0
  40. package/dist/src/cli/index.js +3 -0
  41. package/dist/src/config/convert.d.ts +5 -0
  42. package/dist/src/config/convert.js +48 -0
  43. package/dist/src/config/index.d.ts +6 -0
  44. package/dist/src/config/index.js +25 -0
  45. package/dist/src/config/schema.d.ts +102 -0
  46. package/dist/src/config/schema.js +7 -0
  47. package/dist/src/config/schema.json +209 -0
  48. package/dist/src/config/types.d.ts +43 -0
  49. package/dist/src/config/types.js +1 -0
  50. package/dist/src/config/validation.d.ts +1 -0
  51. package/dist/src/config/validation.js +27 -0
  52. package/dist/src/context.d.ts +3 -0
  53. package/dist/src/context.js +7 -0
  54. package/dist/src/docker/compose/index.d.ts +7 -0
  55. package/dist/src/docker/compose/index.js +13 -0
  56. package/dist/src/docker/containers/getContainer.d.ts +2 -0
  57. package/dist/src/docker/containers/getContainer.js +5 -0
  58. package/dist/src/docker/containers/index.d.ts +1 -0
  59. package/dist/src/docker/containers/index.js +1 -0
  60. package/dist/src/docker/images/buildImage.d.ts +19 -0
  61. package/dist/src/docker/images/buildImage.js +64 -0
  62. package/dist/src/docker/images/deleteImage.d.ts +5 -0
  63. package/dist/src/docker/images/deleteImage.js +6 -0
  64. package/dist/src/docker/images/index.d.ts +4 -0
  65. package/dist/src/docker/images/index.js +4 -0
  66. package/dist/src/docker/images/listImages.d.ts +2 -0
  67. package/dist/src/docker/images/listImages.js +8 -0
  68. package/dist/src/docker/images/pruneImages.d.ts +6 -0
  69. package/dist/src/docker/images/pruneImages.js +8 -0
  70. package/dist/src/docker/index.d.ts +7 -0
  71. package/dist/src/docker/index.js +7 -0
  72. package/dist/src/docker/operations/containers/ListContainersOperation.d.ts +18 -0
  73. package/dist/src/docker/operations/containers/ListContainersOperation.js +44 -0
  74. package/dist/src/docker/operations/containers/PruneContainersOperation.d.ts +16 -0
  75. package/dist/src/docker/operations/containers/PruneContainersOperation.js +33 -0
  76. package/dist/src/docker/operations/containers/index.d.ts +2 -0
  77. package/dist/src/docker/operations/containers/index.js +2 -0
  78. package/dist/src/docker/operations/images/BuildImageOperation.d.ts +20 -0
  79. package/dist/src/docker/operations/images/BuildImageOperation.js +69 -0
  80. package/dist/src/docker/operations/images/ListImagesOperation.d.ts +17 -0
  81. package/dist/src/docker/operations/images/ListImagesOperation.js +38 -0
  82. package/dist/src/docker/operations/images/PruneImagesOperation.d.ts +16 -0
  83. package/dist/src/docker/operations/images/PruneImagesOperation.js +33 -0
  84. package/dist/src/docker/operations/images/index.d.ts +3 -0
  85. package/dist/src/docker/operations/images/index.js +3 -0
  86. package/dist/src/docker/operations/index.d.ts +2 -0
  87. package/dist/src/docker/operations/index.js +2 -0
  88. package/dist/src/docker/protobuf/control.proto +48 -0
  89. package/dist/src/docker/protobuf/index.d.ts +5 -0
  90. package/dist/src/docker/protobuf/index.js +29 -0
  91. package/dist/src/docker/types.d.ts +14 -0
  92. package/dist/src/docker/types.js +1 -0
  93. package/dist/src/docker/utils.d.ts +7 -0
  94. package/dist/src/docker/utils.js +10 -0
  95. package/dist/src/executors/docker.d.ts +6 -0
  96. package/dist/src/executors/docker.js +14 -0
  97. package/dist/src/executors/index.d.ts +6 -0
  98. package/dist/src/executors/index.js +7 -0
  99. package/dist/src/executors/shell.d.ts +2 -0
  100. package/dist/src/executors/shell.js +14 -0
  101. package/dist/src/executors/types.d.ts +8 -0
  102. package/dist/src/executors/types.js +1 -0
  103. package/dist/src/index.d.ts +3 -0
  104. package/dist/src/index.js +3 -0
  105. package/dist/src/monorepo/component.d.ts +24 -0
  106. package/dist/src/monorepo/component.js +77 -0
  107. package/dist/src/monorepo/config.d.ts +16 -0
  108. package/dist/src/monorepo/config.js +66 -0
  109. package/dist/src/monorepo/index.d.ts +7 -0
  110. package/dist/src/monorepo/index.js +7 -0
  111. package/dist/src/monorepo/monorepo.d.ts +29 -0
  112. package/dist/src/monorepo/monorepo.js +106 -0
  113. package/dist/src/monorepo/operations/components/BuildComponentsOperation.d.ts +11 -0
  114. package/dist/src/monorepo/operations/components/BuildComponentsOperation.js +144 -0
  115. package/dist/src/monorepo/operations/components/index.d.ts +1 -0
  116. package/dist/src/monorepo/operations/components/index.js +1 -0
  117. package/dist/src/monorepo/operations/index.d.ts +1 -0
  118. package/dist/src/monorepo/operations/index.js +1 -0
  119. package/dist/src/monorepo/plugins/ComponentsDiscover.d.ts +6 -0
  120. package/dist/src/monorepo/plugins/ComponentsDiscover.js +30 -0
  121. package/dist/src/monorepo/plugins/DotEnvPlugin.d.ts +5 -0
  122. package/dist/src/monorepo/plugins/DotEnvPlugin.js +11 -0
  123. package/dist/src/monorepo/plugins/index.d.ts +7 -0
  124. package/dist/src/monorepo/plugins/index.js +20 -0
  125. package/dist/src/monorepo/plugins/plugin.d.ts +15 -0
  126. package/dist/src/monorepo/plugins/plugin.js +12 -0
  127. package/dist/src/monorepo/project.d.ts +6 -0
  128. package/dist/src/monorepo/project.js +8 -0
  129. package/dist/src/monorepo/store/index.d.ts +20 -0
  130. package/dist/src/monorepo/store/index.js +65 -0
  131. package/dist/src/monorepo/types.d.ts +7 -0
  132. package/dist/src/monorepo/types.js +1 -0
  133. package/dist/src/monorepo/utils/findBuildOrder.d.ts +2 -0
  134. package/dist/src/monorepo/utils/findBuildOrder.js +41 -0
  135. package/dist/src/monorepo/utils/index.d.ts +1 -0
  136. package/dist/src/monorepo/utils/index.js +1 -0
  137. package/dist/src/operations/abstract/AbstractOperation.d.ts +10 -0
  138. package/dist/src/operations/abstract/AbstractOperation.js +13 -0
  139. package/dist/src/operations/abstract/index.d.ts +1 -0
  140. package/dist/src/operations/abstract/index.js +1 -0
  141. package/dist/src/operations/index.d.ts +2 -0
  142. package/dist/src/operations/index.js +2 -0
  143. package/dist/src/operations/types.d.ts +3 -0
  144. package/dist/src/operations/types.js +1 -0
  145. package/dist/src/prerequisites/FilePrerequisitePlugin.d.ts +7 -0
  146. package/dist/src/prerequisites/FilePrerequisitePlugin.js +41 -0
  147. package/dist/src/prerequisites/GitPrerequisitePlugin.d.ts +5 -0
  148. package/dist/src/prerequisites/GitPrerequisitePlugin.js +17 -0
  149. package/dist/src/prerequisites/index.d.ts +3 -0
  150. package/dist/src/prerequisites/index.js +3 -0
  151. package/dist/src/prerequisites/types.d.ts +46 -0
  152. package/dist/src/prerequisites/types.js +24 -0
  153. package/dist/src/types.d.ts +13 -0
  154. package/dist/src/types.js +1 -0
  155. package/dist/src/utils/TemplateExpander.d.ts +21 -0
  156. package/dist/src/utils/TemplateExpander.js +53 -0
  157. package/dist/src/utils/deepMergeArray.d.ts +1 -0
  158. package/dist/src/utils/deepMergeArray.js +19 -0
  159. package/dist/src/utils/index.d.ts +3 -0
  160. package/dist/src/utils/index.js +3 -0
  161. package/dist/src/utils/time.d.ts +2 -0
  162. package/dist/src/utils/time.js +19 -0
  163. package/oclif.manifest.json +572 -2
  164. package/package.json +10 -9
@@ -0,0 +1,30 @@
1
+ import deepmerge from '@fastify/deepmerge';
2
+ import { glob } from 'glob';
3
+ import { dirname } from 'node:path';
4
+ import { MonorepoConfig } from '../index.js';
5
+ import { AbstractPlugin } from './plugin.js';
6
+ export class ComponentDiscoverPlugin extends AbstractPlugin {
7
+ static name = 'autodiscover';
8
+ async extendConfig(config) {
9
+ const files = await glob('*/Dockerfile', {
10
+ cwd: config.project.rootDir,
11
+ });
12
+ const overrides = files.map((path) => {
13
+ const name = dirname(path);
14
+ const component = config.components.find((cmp) => cmp.name === name);
15
+ const cfg = {
16
+ context: name,
17
+ name,
18
+ };
19
+ return component ? deepmerge()(component, cfg) : cfg;
20
+ });
21
+ const untouched = config.components.filter((c) => !overrides.find((o) => {
22
+ return o.name === c.name;
23
+ }));
24
+ const components = [...overrides, ...untouched];
25
+ return new MonorepoConfig({
26
+ ...config,
27
+ components,
28
+ });
29
+ }
30
+ }
@@ -0,0 +1,5 @@
1
+ import { AbstractPlugin } from './plugin.js';
2
+ export declare class DotEnvPlugin extends AbstractPlugin<Array<string>> {
3
+ static name: string;
4
+ init(): Promise<void>;
5
+ }
@@ -0,0 +1,11 @@
1
+ import { configDotenv } from 'dotenv';
2
+ import { AbstractPlugin } from './plugin.js';
3
+ export class DotEnvPlugin extends AbstractPlugin {
4
+ static name = 'dotenv';
5
+ async init() {
6
+ configDotenv({
7
+ path: this.config.map((p) => this.monorepo.join(p)),
8
+ quiet: true,
9
+ });
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ import { AbstractPlugin } from './plugin.js';
2
+ export * from './ComponentsDiscover.js';
3
+ export * from './DotEnvPlugin.js';
4
+ import { Monorepo } from '../monorepo.js';
5
+ export type AbstractPluginConstructor = new <C, P extends AbstractPlugin<C>>(config: C, monorepo: Monorepo) => P;
6
+ export declare const registerPlugin: (plugin: AbstractPluginConstructor) => void;
7
+ export declare const getPlugin: (name: string) => AbstractPluginConstructor;
@@ -0,0 +1,20 @@
1
+ export * from './ComponentsDiscover.js';
2
+ export * from './DotEnvPlugin.js';
3
+ import { ComponentDiscoverPlugin } from './ComponentsDiscover.js';
4
+ import { DotEnvPlugin } from './DotEnvPlugin.js';
5
+ const PluginRegistry = new Map();
6
+ export const registerPlugin = (plugin) => {
7
+ if (PluginRegistry.has(plugin.name)) {
8
+ throw new Error(`Plugin name confict: '${plugin.name}' already registered`);
9
+ }
10
+ PluginRegistry.set(plugin.name, plugin);
11
+ };
12
+ export const getPlugin = (name) => {
13
+ if (!PluginRegistry.has(name)) {
14
+ throw new Error(`Unknown plugin: ${name}`);
15
+ }
16
+ return PluginRegistry.get(name);
17
+ };
18
+ /** Not sure why we need casting */
19
+ registerPlugin(ComponentDiscoverPlugin);
20
+ registerPlugin(DotEnvPlugin);
@@ -0,0 +1,15 @@
1
+ import { Monorepo, MonorepoConfig } from '../index.js';
2
+ export declare abstract class AbstractPlugin<C = unknown> {
3
+ protected config: C;
4
+ protected monorepo: Monorepo;
5
+ /**
6
+ * The name of the plugin (must be unique)
7
+ */
8
+ static name: string;
9
+ constructor(config: C, monorepo: Monorepo);
10
+ extendConfig?(config: MonorepoConfig): Promise<MonorepoConfig>;
11
+ /**
12
+ * Initialization of a plugin.
13
+ */
14
+ init?(): Promise<void>;
15
+ }
@@ -0,0 +1,12 @@
1
+ export class AbstractPlugin {
2
+ config;
3
+ monorepo;
4
+ /**
5
+ * The name of the plugin (must be unique)
6
+ */
7
+ static name;
8
+ constructor(config, monorepo) {
9
+ this.config = config;
10
+ this.monorepo = monorepo;
11
+ }
12
+ }
@@ -0,0 +1,6 @@
1
+ import { IProjectConfig } from '../config/index.js';
2
+ export declare class ProjectConfig implements IProjectConfig {
3
+ name: string;
4
+ rootDir: string;
5
+ constructor(config: IProjectConfig);
6
+ }
@@ -0,0 +1,8 @@
1
+ export class ProjectConfig {
2
+ name;
3
+ rootDir;
4
+ constructor(config) {
5
+ this.name = config.name;
6
+ this.rootDir = config.rootDir;
7
+ }
8
+ }
@@ -0,0 +1,20 @@
1
+ import { Monorepo } from '../index.js';
2
+ /**
3
+ * A first implementation of a "store" where
4
+ * everyone can create things (logs, sentinel files, ...)
5
+ *
6
+ * For now it's a hidden folder on the root of the monorepo
7
+ */
8
+ export declare class EMBStore {
9
+ private monorepo;
10
+ private path;
11
+ constructor(monorepo: Monorepo, dirname?: string);
12
+ createReadStream(path: string): Promise<import("fs").ReadStream>;
13
+ createWriteStream(path: string, flags?: string | undefined): Promise<import("fs").WriteStream>;
14
+ init(): Promise<void>;
15
+ join(path: string): string;
16
+ mkdirp(path: string): Promise<void>;
17
+ readFile(path: string): Promise<Buffer<ArrayBufferLike>>;
18
+ trash(): Promise<void>;
19
+ writeFile(path: string, data: string): Promise<void>;
20
+ }
@@ -0,0 +1,65 @@
1
+ import { constants, createReadStream, createWriteStream } from 'node:fs';
2
+ import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
+ import { dirname, join, normalize } from 'node:path';
4
+ /**
5
+ * A first implementation of a "store" where
6
+ * everyone can create things (logs, sentinel files, ...)
7
+ *
8
+ * For now it's a hidden folder on the root of the monorepo
9
+ */
10
+ export class EMBStore {
11
+ monorepo;
12
+ path;
13
+ constructor(monorepo, dirname = '.emb') {
14
+ this.monorepo = monorepo;
15
+ this.path = this.monorepo.join(dirname);
16
+ }
17
+ async createReadStream(path) {
18
+ await this.mkdirp(dirname(path));
19
+ return createReadStream(this.join(path));
20
+ }
21
+ async createWriteStream(path, flags = 'w') {
22
+ await this.mkdirp(dirname(path));
23
+ return createWriteStream(this.join(path), {
24
+ flags: flags ?? 'w',
25
+ });
26
+ }
27
+ async init() {
28
+ let exists;
29
+ try {
30
+ await access(this.path, constants.F_OK);
31
+ exists = true;
32
+ }
33
+ catch {
34
+ exists = false;
35
+ }
36
+ if (exists) {
37
+ return;
38
+ }
39
+ try {
40
+ await mkdir(this.path, { recursive: true });
41
+ }
42
+ catch (error) {
43
+ const msg = error instanceof Error ? error.message : error;
44
+ throw new Error(`Unable to create the emb store: ${msg}`);
45
+ }
46
+ }
47
+ join(path) {
48
+ return join(this.path, path);
49
+ }
50
+ async mkdirp(path) {
51
+ // Avoid getting out of the store by ensuring nothing goes past ../
52
+ const normalized = normalize(join('/', path));
53
+ await mkdir(this.join(normalized), { recursive: true });
54
+ }
55
+ async readFile(path) {
56
+ return readFile(this.join(path));
57
+ }
58
+ async trash() {
59
+ return rm(this.path, { force: true, recursive: true });
60
+ }
61
+ async writeFile(path, data) {
62
+ await this.mkdirp(dirname(path));
63
+ return writeFile(this.join(path), data);
64
+ }
65
+ }
@@ -0,0 +1,7 @@
1
+ export type TaskInfo = {
2
+ component?: string;
3
+ description?: string;
4
+ id: string;
5
+ name: string;
6
+ script: string;
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Component } from '../index.js';
2
+ export declare const findBuildOrder: (components: Array<Component>, selection?: Array<string>) => Array<Component>;
@@ -0,0 +1,41 @@
1
+ import graphlib from 'graphlib';
2
+ const toGraph = (components) => {
3
+ const graph = new graphlib.Graph();
4
+ // Add all components as nodes
5
+ for (const comp of components) {
6
+ graph.setNode(comp.name);
7
+ }
8
+ // Add edges
9
+ for (const comp of components) {
10
+ for (const dep of comp.dependencies ?? []) {
11
+ graph.setEdge(dep.name, comp.name);
12
+ }
13
+ }
14
+ return graph;
15
+ };
16
+ export const findBuildOrder = (components, selection) => {
17
+ const hash = components.reduce((cmps, cmp) => {
18
+ cmps[cmp.name] = cmp;
19
+ return cmps;
20
+ }, {});
21
+ const graph = toGraph(components);
22
+ // Detect cycles
23
+ const cycles = graphlib.alg.findCycles(graph);
24
+ if (cycles.length > 0) {
25
+ throw new Error('Circular dependencies detected: ' + JSON.stringify(cycles));
26
+ }
27
+ // Pick nodes that we want to build and rebuild a graph only with these
28
+ const toBuild = selection || components.map((c) => c.name);
29
+ const includingDeps = toBuild
30
+ .reduce((set, name) => {
31
+ graph.predecessors(name)?.forEach((name) => {
32
+ set.add(name);
33
+ });
34
+ return set;
35
+ }, new Set(toBuild))
36
+ .values();
37
+ const newGraph = toGraph([...includingDeps].map((name) => hash[name]));
38
+ // Get build order
39
+ const order = graphlib.alg.topsort(newGraph);
40
+ return order.map((name) => hash[name]);
41
+ };
@@ -0,0 +1 @@
1
+ export * from './findBuildOrder.js';
@@ -0,0 +1 @@
1
+ export * from './findBuildOrder.js';
@@ -0,0 +1,10 @@
1
+ import { EmbContext } from '../../index.js';
2
+ import * as z from 'zod';
3
+ import { IOperation } from '../types.js';
4
+ export declare abstract class AbstractOperation<S extends z.Schema, O = unknown> implements IOperation<z.infer<S>, O> {
5
+ protected inputSchema: S;
6
+ protected context: EmbContext;
7
+ constructor(inputSchema: S);
8
+ protected abstract _run(input: z.infer<S>): Promise<O>;
9
+ run(input: z.infer<S>): Promise<O>;
10
+ }
@@ -0,0 +1,13 @@
1
+ import { getContext } from '../../index.js';
2
+ export class AbstractOperation {
3
+ inputSchema;
4
+ context;
5
+ constructor(inputSchema) {
6
+ this.inputSchema = inputSchema;
7
+ this.context = getContext();
8
+ }
9
+ async run(input) {
10
+ const dressed = this.inputSchema.parse(input);
11
+ return this._run(dressed);
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ export * from './AbstractOperation.js';
@@ -0,0 +1 @@
1
+ export * from './AbstractOperation.js';
@@ -0,0 +1,2 @@
1
+ export * from './abstract/index.js';
2
+ export * from './types.js';
@@ -0,0 +1,2 @@
1
+ export * from './abstract/index.js';
2
+ export * from './types.js';
@@ -0,0 +1,3 @@
1
+ export interface IOperation<I = unknown, O = unknown> {
2
+ run(input: I): Promise<O>;
3
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { Component } from '../monorepo/index.js';
2
+ import { FilePrerequisite, PrerequisitePlugin, PrerequisiteType } from './types.js';
3
+ export declare class FilePrerequisitePlugin implements PrerequisitePlugin<PrerequisiteType.file, FilePrerequisite, string> {
4
+ diff(component: Component, prerequisites: Array<FilePrerequisite>, previous: string, _actual: string): Promise<Array<FilePrerequisite> | null>;
5
+ meta(component: Component, prerequisites: FilePrerequisite[], mode: 'post' | 'pre'): Promise<string>;
6
+ private getStats;
7
+ }
@@ -0,0 +1,41 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import pMap from 'p-map';
3
+ export class FilePrerequisitePlugin {
4
+ async diff(component, prerequisites, previous, _actual) {
5
+ const stats = await this.getStats(component, prerequisites);
6
+ const changes = stats
7
+ .filter((s) => {
8
+ return s.mtimeMs > Number.parseInt(previous, 10);
9
+ })
10
+ .map((s) => ({
11
+ path: s.path,
12
+ type: s.type,
13
+ }));
14
+ return changes.length > 0 ? changes : null;
15
+ }
16
+ async meta(component, prerequisites, mode) {
17
+ switch (mode) {
18
+ case 'post': {
19
+ return Date.now().toString();
20
+ }
21
+ case 'pre': {
22
+ const stats = await this.getStats(component, prerequisites);
23
+ const max = stats.reduce((minimum, stat) => {
24
+ return Math.max(minimum, stat.mtimeMs);
25
+ }, 0);
26
+ return max.toFixed(0).toString();
27
+ }
28
+ default: {
29
+ throw new Error(`Invalid mode passed to 'meta()': ${mode}`);
30
+ }
31
+ }
32
+ }
33
+ async getStats(component, prerequisites) {
34
+ return pMap(prerequisites, async (file) => {
35
+ return {
36
+ ...file,
37
+ ...(await stat(component.join(file.path))),
38
+ };
39
+ }, { concurrency: 30 });
40
+ }
41
+ }
@@ -0,0 +1,5 @@
1
+ import { Component } from '../monorepo/index.js';
2
+ import { FilePrerequisite, PrerequisitePlugin, PrerequisiteType } from './types.js';
3
+ export declare class GitPrerequisitePlugin implements PrerequisitePlugin<PrerequisiteType.file, FilePrerequisite> {
4
+ collect(component: Component): Promise<Array<FilePrerequisite>>;
5
+ }
@@ -0,0 +1,17 @@
1
+ import { simpleGit } from 'simple-git';
2
+ import { PrerequisiteType, } from './types.js';
3
+ export class GitPrerequisitePlugin {
4
+ async collect(component) {
5
+ const repo = simpleGit(component.rootdir);
6
+ return (await repo.raw('ls-files', component.rootdir))
7
+ .split('\n')
8
+ .map((s) => s.trim())
9
+ .filter(Boolean)
10
+ .map((path) => {
11
+ return {
12
+ path,
13
+ type: PrerequisiteType.file,
14
+ };
15
+ });
16
+ }
17
+ }
@@ -0,0 +1,3 @@
1
+ export * from './FilePrerequisitePlugin.js';
2
+ export * from './GitPrerequisitePlugin.js';
3
+ export * from './types.js';
@@ -0,0 +1,3 @@
1
+ export * from './FilePrerequisitePlugin.js';
2
+ export * from './GitPrerequisitePlugin.js';
3
+ export * from './types.js';
@@ -0,0 +1,46 @@
1
+ import { Component } from '../monorepo/index.js';
2
+ export declare enum PrerequisiteType {
3
+ file = "file",
4
+ variable = "variable"
5
+ }
6
+ /**
7
+ * A prerequisite must at least include its type name
8
+ */
9
+ export interface Prerequisite<T extends PrerequisiteType | string> {
10
+ type: T;
11
+ }
12
+ /**
13
+ * A file-like prerequisite includes at least its path
14
+ */
15
+ export interface FilePrerequisite extends Prerequisite<PrerequisiteType.file> {
16
+ path: string;
17
+ type: PrerequisiteType.file;
18
+ }
19
+ export interface PrerequisitePlugin<T extends PrerequisiteType, P extends Prerequisite<T>, Output = unknown, Changes = unknown> {
20
+ /**
21
+ * Collect/discover prerequisistes for a specific component
22
+ */
23
+ collect?(component: Component): Promise<Array<P>>;
24
+ /**
25
+ * Returns the list of changes between the last collection and the new
26
+ * collection.
27
+ * Eg:
28
+ * list of files that have changed since the last build
29
+ * list of variables/value that have changed since the last build
30
+ *
31
+ * In case nothing has changed, the plugin must resolve to null
32
+ */
33
+ diff?(component: Component, prerequisistes: Array<P>, previous: Output, actual: Output): Promise<Changes | null>;
34
+ /**
35
+ * Computes the meta-information for a list of prerequisites
36
+ * this meta information will be saved and used later to decide whether
37
+ *
38
+ * The mode specifies if the computation is made before a build is triggered
39
+ * (and before deciding if the build should be triggered)
40
+ * or after a new build has been made
41
+ *
42
+ * or not a component needs to rebuild
43
+ * (eg: some prerequisite files have changed, some vars have changed, etc)
44
+ */
45
+ meta?(component: Component, prerequisites: Array<P>, mode: 'post' | 'pre'): Promise<Output>;
46
+ }
@@ -0,0 +1,24 @@
1
+ // Prerequisites
2
+ export var PrerequisiteType;
3
+ (function (PrerequisiteType) {
4
+ PrerequisiteType["file"] = "file";
5
+ PrerequisiteType["variable"] = "variable";
6
+ })(PrerequisiteType || (PrerequisiteType = {}));
7
+ // Checker for files:
8
+ //
9
+ // Collect all files a component depends on
10
+ // and compute their last updated time
11
+ //
12
+ // No previous build available for this checker?
13
+ // -> build and then return now() as output
14
+ // Previous build? receive the last output (now())
15
+ // and compare it with most recent prerequisite.
16
+ // Checker for variables:
17
+ //
18
+ // Collect all variables a component depends on
19
+ // and compute a hash key=>value
20
+ //
21
+ // No previous build available for this checker?
22
+ // -> build and then return the hash as output
23
+ // Previous build? receive the last output (hash)
24
+ // and compare it with current hash -> different ? true : false
@@ -0,0 +1,13 @@
1
+ import type Docker from 'dockerode';
2
+ import { Monorepo } from './monorepo/index.js';
3
+ /**
4
+ * The context is meant to be what all plugins can decorate
5
+ * to install their own things
6
+ *
7
+ * Similar to Request in Expressjs projects, feel free to extend the type
8
+ * and install here things that to be accessible by operations during a CLI run
9
+ */
10
+ export interface EmbContext {
11
+ docker: Docker;
12
+ monorepo: Monorepo;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ type ExpandOptions = {
2
+ default?: string;
3
+ sources?: Record<string, Record<string, unknown>>;
4
+ };
5
+ export type ExpansionHistory = {
6
+ source: string;
7
+ value: unknown;
8
+ variable: string;
9
+ };
10
+ export declare class TemplateExpander {
11
+ /**
12
+ * Keep track of the sources used for expansions
13
+ * (track source, name, final value)
14
+ */
15
+ private expansions;
16
+ get expansionCount(): number;
17
+ expand(str: string, options?: ExpandOptions): Promise<string>;
18
+ expandRecord<R extends Record<string, unknown>>(record: R, options?: ExpandOptions): Promise<R>;
19
+ private track;
20
+ }
21
+ export {};
@@ -0,0 +1,53 @@
1
+ const TPL_REGEX = /(?<!\\)\${(?:(\w+):)?(\w+)(?::-(.*?))?}/g;
2
+ export class TemplateExpander {
3
+ /**
4
+ * Keep track of the sources used for expansions
5
+ * (track source, name, final value)
6
+ */
7
+ expansions = [];
8
+ get expansionCount() {
9
+ return this.expansions.length;
10
+ }
11
+ async expand(str, options = {}) {
12
+ return ((str || '')
13
+ .toString()
14
+ // Expand variables
15
+ .replaceAll(TPL_REGEX, (match, source, key, fallback) => {
16
+ const src = source || options.default;
17
+ const provider = options.sources?.[src];
18
+ if (!provider) {
19
+ if (fallback !== undefined) {
20
+ return this.track(src, key, fallback);
21
+ }
22
+ throw new Error(`Invalid expand provider '${source}' ('${match}')`);
23
+ }
24
+ const val = provider[key];
25
+ // fallback is undefined when not even the :- is present
26
+ // we consider a variable like ${source:key:-} like the information
27
+ // that the variable can be an empty string if not present
28
+ if (!val && fallback === undefined) {
29
+ throw new Error(`Could not expand '${match}' and no default value provided`);
30
+ }
31
+ if (val !== undefined && val !== null) {
32
+ return this.track(src, key, val);
33
+ }
34
+ return this.track(src, key, fallback || '');
35
+ })
36
+ // Unescape non-variables left
37
+ .replaceAll('\\${', '${'));
38
+ }
39
+ async expandRecord(record, options = {}) {
40
+ return Object.entries(record).reduce(async (vars, [name, str]) => {
41
+ const previous = await vars;
42
+ // @ts-expect-error dunno
43
+ previous[name] = await (typeof str === 'object'
44
+ ? this.expandRecord(str, options)
45
+ : this.expand(str, options));
46
+ return previous;
47
+ }, Promise.resolve({}));
48
+ }
49
+ track(source, variable, value) {
50
+ this.expansions.push({ source, value, variable });
51
+ return value;
52
+ }
53
+ }
@@ -0,0 +1 @@
1
+ export declare const deepMergeArray: <T>(target: Array<T>, source: Array<T>, identifierFn?: (item: T) => unknown) => Array<T>;
@@ -0,0 +1,19 @@
1
+ import deepmerge from '@fastify/deepmerge';
2
+ export const deepMergeArray = (target, source, identifierFn) => {
3
+ if (!identifierFn) {
4
+ return deepmerge()(target, source);
5
+ }
6
+ const overridden = target.map((item) => {
7
+ const id = identifierFn(item);
8
+ const override = source.find((item) => identifierFn(item) === id);
9
+ if (override) {
10
+ return deepmerge()(item, override);
11
+ }
12
+ return item;
13
+ });
14
+ const additional = source.filter((item) => {
15
+ const id = identifierFn(item);
16
+ return !target.find((item) => identifierFn(item) === id);
17
+ });
18
+ return [...overridden, ...additional];
19
+ };
@@ -0,0 +1,3 @@
1
+ export * from './deepMergeArray.js';
2
+ export * from './TemplateExpander.js';
3
+ export * from './time.js';
@@ -0,0 +1,3 @@
1
+ export * from './deepMergeArray.js';
2
+ export * from './TemplateExpander.js';
3
+ export * from './time.js';
@@ -0,0 +1,2 @@
1
+ import { DateTime } from 'luxon';
2
+ export declare const timeAgo: (date: Date | DateTime) => string;
@@ -0,0 +1,19 @@
1
+ import { DateTime } from 'luxon';
2
+ const units = [
3
+ 'year',
4
+ 'month',
5
+ 'week',
6
+ 'day',
7
+ 'hour',
8
+ 'minute',
9
+ 'second',
10
+ ];
11
+ export const timeAgo = (date) => {
12
+ const dateTime = date instanceof Date ? DateTime.fromJSDate(date) : date;
13
+ const diff = dateTime.diffNow().shiftTo(...units);
14
+ const unit = units.find((unit) => diff.get(unit) !== 0) || 'second';
15
+ const relativeFormatter = new Intl.RelativeTimeFormat('en', {
16
+ numeric: 'auto',
17
+ });
18
+ return relativeFormatter.format(Math.trunc(diff.as(unit)), unit);
19
+ };