@gezelligate/core 0.1.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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -0
  3. package/dist/bridgeManifest.d.ts +67 -0
  4. package/dist/bridgeManifest.d.ts.map +1 -0
  5. package/dist/bridgeManifest.js +35 -0
  6. package/dist/bridgeManifest.js.map +1 -0
  7. package/dist/capabilities.d.ts +23 -0
  8. package/dist/capabilities.d.ts.map +1 -0
  9. package/dist/capabilities.js +15 -0
  10. package/dist/capabilities.js.map +1 -0
  11. package/dist/catalog/bootstrap.d.ts +25 -0
  12. package/dist/catalog/bootstrap.d.ts.map +1 -0
  13. package/dist/catalog/bootstrap.js +56 -0
  14. package/dist/catalog/bootstrap.js.map +1 -0
  15. package/dist/catalog/index.d.ts +4 -0
  16. package/dist/catalog/index.d.ts.map +1 -0
  17. package/dist/catalog/index.js +4 -0
  18. package/dist/catalog/index.js.map +1 -0
  19. package/dist/catalog/projectPin.d.ts +22 -0
  20. package/dist/catalog/projectPin.d.ts.map +1 -0
  21. package/dist/catalog/projectPin.js +39 -0
  22. package/dist/catalog/projectPin.js.map +1 -0
  23. package/dist/catalog/reader.d.ts +44 -0
  24. package/dist/catalog/reader.d.ts.map +1 -0
  25. package/dist/catalog/reader.js +7 -0
  26. package/dist/catalog/reader.js.map +1 -0
  27. package/dist/catalog/tarball.d.ts +48 -0
  28. package/dist/catalog/tarball.d.ts.map +1 -0
  29. package/dist/catalog/tarball.js +114 -0
  30. package/dist/catalog/tarball.js.map +1 -0
  31. package/dist/cluster.d.ts +4 -0
  32. package/dist/cluster.d.ts.map +1 -0
  33. package/dist/cluster.js +46 -0
  34. package/dist/cluster.js.map +1 -0
  35. package/dist/dedicatedPeer.d.ts +43 -0
  36. package/dist/dedicatedPeer.d.ts.map +1 -0
  37. package/dist/dedicatedPeer.js +38 -0
  38. package/dist/dedicatedPeer.js.map +1 -0
  39. package/dist/dependencies.d.ts +6 -0
  40. package/dist/dependencies.d.ts.map +1 -0
  41. package/dist/dependencies.js +124 -0
  42. package/dist/dependencies.js.map +1 -0
  43. package/dist/index.d.ts +15 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +17 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/ingress.d.ts +4 -0
  48. package/dist/ingress.d.ts.map +1 -0
  49. package/dist/ingress.js +17 -0
  50. package/dist/ingress.js.map +1 -0
  51. package/dist/keycloak.d.ts +61 -0
  52. package/dist/keycloak.d.ts.map +1 -0
  53. package/dist/keycloak.js +161 -0
  54. package/dist/keycloak.js.map +1 -0
  55. package/dist/numericId.d.ts +2 -0
  56. package/dist/numericId.d.ts.map +1 -0
  57. package/dist/numericId.js +22 -0
  58. package/dist/numericId.js.map +1 -0
  59. package/dist/providers/lifecycle.d.ts +81 -0
  60. package/dist/providers/lifecycle.d.ts.map +1 -0
  61. package/dist/providers/lifecycle.js +22 -0
  62. package/dist/providers/lifecycle.js.map +1 -0
  63. package/dist/providers.d.ts +4 -0
  64. package/dist/providers.d.ts.map +1 -0
  65. package/dist/providers.js +45 -0
  66. package/dist/providers.js.map +1 -0
  67. package/dist/render.d.ts +22 -0
  68. package/dist/render.d.ts.map +1 -0
  69. package/dist/render.js +354 -0
  70. package/dist/render.js.map +1 -0
  71. package/dist/repository.d.ts +4 -0
  72. package/dist/repository.d.ts.map +1 -0
  73. package/dist/repository.js +31 -0
  74. package/dist/repository.js.map +1 -0
  75. package/dist/schema/clusterYaml.d.ts +39 -0
  76. package/dist/schema/clusterYaml.d.ts.map +1 -0
  77. package/dist/schema/clusterYaml.js +24 -0
  78. package/dist/schema/clusterYaml.js.map +1 -0
  79. package/dist/schema/configYaml.d.ts +90 -0
  80. package/dist/schema/configYaml.d.ts.map +1 -0
  81. package/dist/schema/configYaml.js +32 -0
  82. package/dist/schema/configYaml.js.map +1 -0
  83. package/dist/schema/providerYaml.d.ts +312 -0
  84. package/dist/schema/providerYaml.d.ts.map +1 -0
  85. package/dist/schema/providerYaml.js +54 -0
  86. package/dist/schema/providerYaml.js.map +1 -0
  87. package/dist/schema/serviceYaml.d.ts +2407 -0
  88. package/dist/schema/serviceYaml.d.ts.map +1 -0
  89. package/dist/schema/serviceYaml.js +200 -0
  90. package/dist/schema/serviceYaml.js.map +1 -0
  91. package/dist/secrets.d.ts +4 -0
  92. package/dist/secrets.d.ts.map +1 -0
  93. package/dist/secrets.js +31 -0
  94. package/dist/secrets.js.map +1 -0
  95. package/dist/secretsSummary.d.ts +2 -0
  96. package/dist/secretsSummary.d.ts.map +1 -0
  97. package/dist/secretsSummary.js +50 -0
  98. package/dist/secretsSummary.js.map +1 -0
  99. package/dist/services.d.ts +7 -0
  100. package/dist/services.d.ts.map +1 -0
  101. package/dist/services.js +66 -0
  102. package/dist/services.js.map +1 -0
  103. package/dist/sharedDb.d.ts +3 -0
  104. package/dist/sharedDb.d.ts.map +1 -0
  105. package/dist/sharedDb.js +104 -0
  106. package/dist/sharedDb.js.map +1 -0
  107. package/dist/target.d.ts +35 -0
  108. package/dist/target.d.ts.map +1 -0
  109. package/dist/target.js +7 -0
  110. package/dist/target.js.map +1 -0
  111. package/dist/templates/dedicated-postgres.docker.yaml.tmpl +12 -0
  112. package/dist/templates/dedicated-postgres.k8s.yaml.tmpl +57 -0
  113. package/dist/templates/dedicated-redis.docker.yaml.tmpl +9 -0
  114. package/dist/templates/dedicated-redis.k8s.yaml.tmpl +46 -0
  115. package/dist/templating.d.ts +2 -0
  116. package/dist/templating.d.ts.map +1 -0
  117. package/dist/templating.js +15 -0
  118. package/dist/templating.js.map +1 -0
  119. package/package.json +65 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceYaml.d.ts","sourceRoot":"","sources":["../../src/schema/serviceYaml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQ1B,CAAC;AAEH,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;EAKpB,CAAC;AAEH,QAAA,MAAM,YAAY;;;;;;;;;;;;EAIhB,CAAC;AAQH,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAMT,CAAC;AAMZ,QAAA,MAAM,WAAW;;;;;;EAEN,CAAC;AAuHZ,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMhB,CAAC;AAEH,QAAA,MAAM,cAAc;;;;;;EAElB,CAAC;AAYH,QAAA,MAAM,kBAAkB;;;;;;;;;EAGtB,CAAC;AAEH,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASlB,CAAC;AAoBH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAChD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,200 @@
1
+ import { z } from "zod";
2
+ export const FormFieldSchema = z.object({
3
+ key: z.string().min(1),
4
+ label: z.string().min(1),
5
+ type: z.enum(["string", "email", "boolean", "number", "select"]),
6
+ default: z.union([z.string(), z.number(), z.boolean()]).optional(),
7
+ required: z.boolean().optional(),
8
+ help: z.string().optional(),
9
+ options: z.array(z.object({ value: z.string(), label: z.string() })).optional()
10
+ });
11
+ const DependencySchema = z.object({
12
+ service: z.string().min(1),
13
+ reason: z.string().min(1),
14
+ required: z.boolean(),
15
+ when: z.string().optional()
16
+ });
17
+ const SecretSchema = z.object({
18
+ key: z.string().regex(/^[A-Z][A-Z0-9_]*$/, "must be UPPER_SNAKE_CASE"),
19
+ generator: z.enum(["password", "hex", "uuid"]),
20
+ length: z.number().int().positive().optional()
21
+ });
22
+ // Services declare a database NEED with `preferShared` (default true). The
23
+ // renderer decides where that need is satisfied by matching against a service
24
+ // that provides `sharedDatabase` (when preferShared is true and such a
25
+ // provider is in the dependency closure). When no provider exists, or when
26
+ // preferShared is false, the renderer falls back to a per-service sidecar
27
+ // using the optional `storage` field.
28
+ const DatabaseSchema = z.object({
29
+ engine: z.literal("postgres"),
30
+ preferShared: z.boolean().default(true),
31
+ name: z.string().min(1),
32
+ user: z.string().min(1),
33
+ storage: z.string().optional()
34
+ }).strict();
35
+ // Services declare a redis NEED with `preferShared` (default true). The
36
+ // renderer routes the service's Redis traffic to whichever service provides
37
+ // `sharedRedis` in the dependency closure. Consumers must namespace their
38
+ // keys; every consumer uses logical DB 0.
39
+ const RedisSchema = z.object({
40
+ preferShared: z.boolean().default(true)
41
+ }).strict();
42
+ const OidcClientSchema = z.object({
43
+ clientId: z.string().min(1),
44
+ redirectUris: z.array(z.string().min(1)),
45
+ scopes: z.array(z.string().min(1)),
46
+ // Shape of the `/userinfo` response we ask Keycloak to emit for this
47
+ // client. "oidc" is the standard set of OIDC claims (sub, preferred_username,
48
+ // email, ...). "gitlab" mimics GitLab's `/api/v4/user` shape (numeric `id`,
49
+ // `username`, `name`, `email`) — required by Mattermost Team Edition's
50
+ // `gitlab` OAuth provider, which is the only provider compiled into the
51
+ // open-source Mattermost binary.
52
+ userinfoFormat: z.enum(["oidc", "gitlab"]).optional(),
53
+ // When true, the realm-import enables Keycloak's standard token-exchange
54
+ // grant on this client. Any service whose backend exchanges its session
55
+ // token for vendor-audience tokens (e.g. the bridge) opts in here.
56
+ tokenExchange: z.boolean().optional()
57
+ });
58
+ const IngressSchema = z.object({
59
+ host: z.string().min(1),
60
+ port: z.number().int().positive(),
61
+ // Optional override for the Kubernetes backend Service. Defaults: service
62
+ // name equals the gezelligate service name; port equals `ingress.port`.
63
+ // Helm charts that name their Service something other than the release
64
+ // (e.g. keycloakx creates `<release>-keycloakx-http`) must set this.
65
+ k8s: z
66
+ .object({
67
+ service: z.string().min(1).optional(),
68
+ port: z.number().int().positive().optional(),
69
+ // Optional path-based overrides. Each entry routes requests
70
+ // whose path starts with `prefix` to a different Service+port,
71
+ // higher priority than the default catch-all route. Used by
72
+ // multi-component services (e.g. meet — SPA at /, Django API
73
+ // at /api/*) so a single Host() can fan out across backends.
74
+ pathOverrides: z
75
+ .array(z.object({
76
+ prefix: z.string().min(1),
77
+ service: z.string().min(1),
78
+ port: z.number().int().positive()
79
+ }))
80
+ .optional()
81
+ })
82
+ .optional()
83
+ });
84
+ const BridgeTileSchema = z.object({
85
+ name: z.string().min(1),
86
+ icon: z.string().min(1),
87
+ url: z.string().min(1)
88
+ });
89
+ const BridgeActivitySchema = z.object({
90
+ api: z.object({
91
+ unreadEndpoint: z.string().min(1),
92
+ auth: z.enum(["oidc-passthrough", "service-token"])
93
+ })
94
+ });
95
+ const BridgeRecentItemsSchema = z.object({
96
+ api: z.object({
97
+ endpoint: z.string().min(1),
98
+ limit: z.number().int().positive(),
99
+ mapping: z.object({
100
+ id: z.string().min(1),
101
+ title: z.string().min(1),
102
+ url: z.string().min(1),
103
+ timestamp: z.string().min(1)
104
+ })
105
+ })
106
+ });
107
+ const BridgeDbReadSchema = z.object({
108
+ tables: z.array(z.string().min(1)).min(1)
109
+ });
110
+ const BridgeSearchApiSchema = z.object({
111
+ method: z.enum(["GET", "POST"]),
112
+ endpoint: z.string().min(1),
113
+ body: z.string().optional(),
114
+ auth: z.enum(["oidc-passthrough", "service-token"]),
115
+ mapping: z.object({
116
+ id: z.string().min(1),
117
+ title: z.string().min(1),
118
+ url: z.string().min(1),
119
+ timestamp: z.string().min(1),
120
+ snippet: z.string().min(1).optional()
121
+ })
122
+ });
123
+ const SqlColumnIdent = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "must be a SQL column identifier");
124
+ const BridgeSearchIndexSchema = z.object({
125
+ table: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/, "table must be a SQL identifier (schema.name or name)"),
126
+ where: z.string().optional(),
127
+ watermark: SqlColumnIdent,
128
+ fields: z.object({
129
+ id: SqlColumnIdent,
130
+ title: SqlColumnIdent,
131
+ body: SqlColumnIdent,
132
+ url: SqlColumnIdent,
133
+ timestamp: SqlColumnIdent
134
+ })
135
+ });
136
+ const BridgeSearchSchema = z.object({
137
+ api: BridgeSearchApiSchema.optional(),
138
+ index: BridgeSearchIndexSchema.optional()
139
+ }).refine((s) => s.api !== undefined || s.index !== undefined, { message: "bridge.search must declare at least one of api or index" });
140
+ const BridgeSchema = z.object({
141
+ tile: BridgeTileSchema,
142
+ activity: BridgeActivitySchema.optional(),
143
+ recentItems: BridgeRecentItemsSchema.optional(),
144
+ dbRead: BridgeDbReadSchema.optional(),
145
+ search: BridgeSearchSchema.optional()
146
+ });
147
+ const UserSeedSchema = z.object({
148
+ passwordEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/, "must be UPPER_SNAKE_CASE")
149
+ });
150
+ const OidcServerSchema = z.object({
151
+ realmEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional()
152
+ });
153
+ const SharedDatabaseSchema = z.object({
154
+ engine: z.literal("postgres")
155
+ });
156
+ const SharedRedisSchema = z.object({}).default({});
157
+ const BridgeReaderSchema = z.object({
158
+ roleName: z.string().min(1),
159
+ passwordEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/, "must be UPPER_SNAKE_CASE")
160
+ });
161
+ const ProvidesSchema = z.object({
162
+ oidcClient: OidcClientSchema.optional(),
163
+ oidcServer: OidcServerSchema.optional(),
164
+ userSeed: UserSeedSchema.optional(),
165
+ ingress: IngressSchema.optional(),
166
+ bridge: BridgeSchema.optional(),
167
+ bridgeReader: BridgeReaderSchema.optional(),
168
+ sharedDatabase: SharedDatabaseSchema.optional(),
169
+ sharedRedis: SharedRedisSchema.optional()
170
+ });
171
+ const DockerTargetSchema = z.object({
172
+ template: z.string().min(1)
173
+ });
174
+ const KubernetesTargetSchema = z.object({
175
+ helmChart: z.string().min(1),
176
+ helmVersion: z.string().min(1),
177
+ valuesTemplate: z.string().min(1)
178
+ });
179
+ const TargetsSchema = z.object({
180
+ docker: DockerTargetSchema.optional(),
181
+ kubernetes: KubernetesTargetSchema.optional()
182
+ }).refine((t) => t.docker !== undefined || t.kubernetes !== undefined, { message: "at least one target (docker or kubernetes) must be defined" });
183
+ export const ServiceYamlSchema = z.object({
184
+ name: z.string().regex(/^[a-z][a-z0-9-]*$/, "must be kebab-case"),
185
+ displayName: z.string().min(1),
186
+ description: z.string(),
187
+ category: z.string().min(1),
188
+ required: z.boolean(),
189
+ hidden: z.boolean().optional(),
190
+ kernel: z.boolean().optional(),
191
+ icon: z.string().optional(),
192
+ form: z.array(FormFieldSchema),
193
+ dependencies: z.array(DependencySchema),
194
+ secrets: z.array(SecretSchema),
195
+ database: DatabaseSchema.optional(),
196
+ redis: RedisSchema.optional(),
197
+ provides: ProvidesSchema,
198
+ targets: TargetsSchema
199
+ });
200
+ //# sourceMappingURL=serviceYaml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceYaml.js","sourceRoot":"","sources":["../../src/schema/serviceYaml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Secret } from "./schema/serviceYaml.js";
2
+ export declare function generateSecret(secret: Secret): string;
3
+ export declare function generateMissingSecrets(declared: Secret[], existing: Record<string, string>): Record<string, string>;
4
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAItD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBrD;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
@@ -0,0 +1,31 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ const PASSWORD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
3
+ export function generateSecret(secret) {
4
+ switch (secret.generator) {
5
+ case "password": {
6
+ const length = secret.length ?? 32;
7
+ const bytes = randomBytes(length);
8
+ let out = "";
9
+ for (let i = 0; i < length; i++) {
10
+ out += PASSWORD_ALPHABET[bytes[i] % PASSWORD_ALPHABET.length];
11
+ }
12
+ return out;
13
+ }
14
+ case "hex": {
15
+ const length = secret.length ?? 32;
16
+ return randomBytes(Math.ceil(length / 2)).toString("hex").slice(0, length);
17
+ }
18
+ case "uuid":
19
+ return randomUUID();
20
+ }
21
+ }
22
+ export function generateMissingSecrets(declared, existing) {
23
+ const out = { ...existing };
24
+ for (const s of declared) {
25
+ if (!(s.key in out)) {
26
+ out[s.key] = generateSecret(s);
27
+ }
28
+ }
29
+ return out;
30
+ }
31
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGtD,MAAM,iBAAiB,GAAG,kEAAkE,CAAC;AAE7F,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,QAAQ,MAAM,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChC,GAAG,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACjE,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;YACnC,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7E,CAAC;QACD,KAAK,MAAM;YACT,OAAO,UAAU,EAAE,CAAC;IACxB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,QAAkB,EAClB,QAAgC;IAEhC,MAAM,GAAG,GAA2B,EAAE,GAAG,QAAQ,EAAE,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;YACpB,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function buildSecretsSummary(envByService: Record<string, Record<string, string>>, hiddenServices: ReadonlySet<string>): string;
2
+ //# sourceMappingURL=secretsSummary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretsSummary.d.ts","sourceRoot":"","sources":["../src/secretsSummary.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACpD,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,MAAM,CAqDR"}
@@ -0,0 +1,50 @@
1
+ export function buildSecretsSummary(envByService, hiddenServices) {
2
+ const lines = [];
3
+ lines.push("# Secrets Summary");
4
+ lines.push("");
5
+ lines.push("> **Save these to a password manager now.** This file is regenerated on every build and is gitignored. The secrets it contains are required to administer your stack.");
6
+ lines.push("");
7
+ const keycloakEnv = envByService["keycloak"] ?? {};
8
+ const userPasswords = Object.entries(keycloakEnv)
9
+ .filter(([k]) => k.startsWith("USER_") && k.endsWith("_TEMP_PASSWORD"))
10
+ .sort();
11
+ if (userPasswords.length > 0) {
12
+ lines.push("## Initial user first-login credentials");
13
+ lines.push("");
14
+ lines.push("> These are **temporary** passwords. Each user will be prompted to set a new password on their first login; after that, Keycloak stores only the hash and these values become meaningless. Share each password with its user via a secure channel.");
15
+ lines.push("");
16
+ for (const [k, v] of userPasswords) {
17
+ // USER_ALICE_TEMP_PASSWORD → alice
18
+ const username = k.slice("USER_".length, -"_TEMP_PASSWORD".length).toLowerCase();
19
+ lines.push(`- **${username}**: \`${v}\``);
20
+ }
21
+ lines.push("");
22
+ }
23
+ const names = Object.keys(envByService).sort();
24
+ const infraNames = names.filter((n) => hiddenServices.has(n));
25
+ const appNames = names.filter((n) => !hiddenServices.has(n));
26
+ if (infraNames.length > 0) {
27
+ lines.push("## Shared infrastructure");
28
+ lines.push("");
29
+ lines.push("> These credentials administer the cluster-wide services (like the shared Postgres superuser). Back them up first.");
30
+ lines.push("");
31
+ for (const n of infraNames) {
32
+ lines.push(`### ${n}`);
33
+ lines.push("");
34
+ for (const [k, v] of Object.entries(envByService[n] ?? {}).sort()) {
35
+ lines.push(`- ${k}: \`${v}\``);
36
+ }
37
+ lines.push("");
38
+ }
39
+ }
40
+ for (const n of appNames) {
41
+ lines.push(`## ${n}`);
42
+ lines.push("");
43
+ for (const [k, v] of Object.entries(envByService[n] ?? {}).sort()) {
44
+ lines.push(`- ${k}: \`${v}\``);
45
+ }
46
+ lines.push("");
47
+ }
48
+ return lines.join("\n");
49
+ }
50
+ //# sourceMappingURL=secretsSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretsSummary.js","sourceRoot":"","sources":["../src/secretsSummary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type ConfigYaml } from "./schema/configYaml.js";
2
+ export type Services = Map<string, ConfigYaml>;
3
+ export declare function loadServices(rootDir: string): Promise<Services>;
4
+ export declare function writeServiceConfig(rootDir: string, name: string, config: ConfigYaml): Promise<void>;
5
+ export declare function loadEnv(rootDir: string, name: string): Promise<Record<string, string>>;
6
+ export declare function writeEnv(rootDir: string, name: string, env: Record<string, string>): Promise<void>;
7
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE3E,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAE/C,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqBrE;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkB5F;AAED,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,66 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import yaml from "js-yaml";
4
+ import { ConfigYamlSchema } from "./schema/configYaml.js";
5
+ export async function loadServices(rootDir) {
6
+ const out = new Map();
7
+ const entries = await fs.readdir(rootDir, { withFileTypes: true }).catch((err) => {
8
+ if (err.code === "ENOENT")
9
+ return null;
10
+ throw err;
11
+ });
12
+ if (entries === null)
13
+ return out;
14
+ for (const entry of entries) {
15
+ if (!entry.isDirectory())
16
+ continue;
17
+ const cfgPath = path.join(rootDir, entry.name, "config.yaml");
18
+ let raw;
19
+ try {
20
+ raw = await fs.readFile(cfgPath, "utf8");
21
+ }
22
+ catch (err) {
23
+ if (err.code === "ENOENT")
24
+ continue;
25
+ throw err;
26
+ }
27
+ const parsed = ConfigYamlSchema.parse(yaml.load(raw));
28
+ out.set(entry.name, parsed);
29
+ }
30
+ return out;
31
+ }
32
+ export async function writeServiceConfig(rootDir, name, config) {
33
+ const dir = path.join(rootDir, name);
34
+ await fs.mkdir(dir, { recursive: true });
35
+ await fs.writeFile(path.join(dir, "config.yaml"), yaml.dump(config), "utf8");
36
+ }
37
+ export async function loadEnv(rootDir, name) {
38
+ const envPath = path.join(rootDir, name, ".env");
39
+ let raw;
40
+ try {
41
+ raw = await fs.readFile(envPath, "utf8");
42
+ }
43
+ catch (err) {
44
+ if (err.code === "ENOENT")
45
+ return {};
46
+ throw err;
47
+ }
48
+ const out = {};
49
+ for (const line of raw.split("\n")) {
50
+ const trimmed = line.trim();
51
+ if (!trimmed || trimmed.startsWith("#"))
52
+ continue;
53
+ const eq = trimmed.indexOf("=");
54
+ if (eq === -1)
55
+ continue;
56
+ out[trimmed.slice(0, eq)] = trimmed.slice(eq + 1);
57
+ }
58
+ return out;
59
+ }
60
+ export async function writeEnv(rootDir, name, env) {
61
+ const dir = path.join(rootDir, name);
62
+ await fs.mkdir(dir, { recursive: true });
63
+ const lines = Object.entries(env).map(([k, v]) => `${k}=${v}`);
64
+ await fs.writeFile(path.join(dir, ".env"), lines.join("\n") + "\n", "utf8");
65
+ }
66
+ //# sourceMappingURL=services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.js","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ServiceState } from "./keycloak.js";
2
+ export declare function buildInitSql(states: ServiceState[]): string;
3
+ //# sourceMappingURL=sharedDb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharedDb.d.ts","sourceRoot":"","sources":["../src/sharedDb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAwBlD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAgH3D"}
@@ -0,0 +1,104 @@
1
+ import { findOne } from "./capabilities.js";
2
+ // Postgres identifiers: letters, digits, underscores; max 63 bytes. We enforce
3
+ // a safe subset because db.name / db.user are free-form strings in the schema.
4
+ // Valid chars: [A-Za-z_][A-Za-z0-9_]*
5
+ const IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
6
+ function assertIdent(kind, value) {
7
+ if (!IDENT_RE.test(value)) {
8
+ throw new Error(`sharedDb: invalid ${kind} ${JSON.stringify(value)} — must match ${IDENT_RE.source}`);
9
+ }
10
+ }
11
+ function assertSafePassword(value) {
12
+ if (value.includes("'") || value.includes("\\")) {
13
+ throw new Error("sharedDb: password contains single-quote or backslash — cannot safely embed in init SQL");
14
+ }
15
+ }
16
+ export function buildInitSql(states) {
17
+ const shared = states.filter((s) => s.def.database !== undefined && (s.db?.mode ?? (s.def.database.preferShared ? "shared" : "dedicated")) === "shared");
18
+ if (shared.length === 0)
19
+ return "";
20
+ const stanzas = [];
21
+ for (const s of shared) {
22
+ const db = s.def.database;
23
+ const password = s.env.DB_PASSWORD;
24
+ if (!password) {
25
+ throw new Error(`sharedDb: service ${s.def.name} has database.preferShared but no DB_PASSWORD in env`);
26
+ }
27
+ assertIdent("database name", db.name);
28
+ assertIdent("database user", db.user);
29
+ assertSafePassword(password);
30
+ stanzas.push(`-- ${s.def.name}
31
+ DO $$
32
+ BEGIN
33
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${db.user}') THEN
34
+ CREATE USER ${db.user} WITH PASSWORD '${password}';
35
+ END IF;
36
+ END$$;
37
+
38
+ SELECT 'CREATE DATABASE ${db.name} OWNER ${db.user}'
39
+ WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${db.name}')\\gexec
40
+ `);
41
+ }
42
+ // Helper to collect tables from both dbRead.tables and search.index.table
43
+ function readerTablesFor(s) {
44
+ const tables = new Set();
45
+ // Add tables from explicit dbRead declaration
46
+ for (const t of s.def.provides.bridge?.dbRead?.tables ?? []) {
47
+ tables.add(t);
48
+ }
49
+ // Add table from search.index if present
50
+ const idx = s.def.provides.bridge?.search?.index;
51
+ if (idx) {
52
+ tables.add(idx.table);
53
+ }
54
+ return [...tables];
55
+ }
56
+ // Handle bridge_reader grants for services that declare provides.bridge.dbRead
57
+ // or provides.bridge.search.index.table. The reader role identity (name +
58
+ // password env) comes from whichever service declares `provides.bridgeReader`
59
+ // — looked up by capability so the engine never depends on the string "bridge".
60
+ const readerServices = states.filter((s) => readerTablesFor(s).length > 0);
61
+ if (readerServices.length > 0) {
62
+ const readerProvider = findOne(states, "bridgeReader");
63
+ const readerCap = readerProvider?.def.provides.bridgeReader;
64
+ const readerPw = readerProvider && readerCap
65
+ ? readerProvider.env[readerCap.passwordEnv]
66
+ : undefined;
67
+ if (!readerProvider || !readerCap || !readerPw) {
68
+ throw new Error("sharedDb: a service declares provides.bridge.dbRead or provides.bridge.search.index but no enabled service declares provides.bridgeReader with a populated password env");
69
+ }
70
+ assertSafePassword(readerPw);
71
+ const readerRole = readerCap.roleName;
72
+ assertIdent("bridgeReader.roleName", readerRole);
73
+ stanzas.push(`-- ${readerRole} (cross-DB SELECT for the launcher's indexer + aggregate widgets)
74
+ DO $$
75
+ BEGIN
76
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${readerRole}') THEN
77
+ CREATE USER ${readerRole} WITH PASSWORD '${readerPw}';
78
+ END IF;
79
+ END$$;
80
+ `);
81
+ for (const s of readerServices) {
82
+ const db = s.def.database;
83
+ if (!db) {
84
+ throw new Error(`sharedDb: service ${s.def.name} declares provides.bridge.dbRead or provides.bridge.search.index but has no database`);
85
+ }
86
+ assertIdent("database name", db.name);
87
+ const tables = readerTablesFor(s);
88
+ const grants = [];
89
+ grants.push(`GRANT CONNECT ON DATABASE ${db.name} TO ${readerRole};`);
90
+ grants.push(`\\c ${db.name}`);
91
+ grants.push(`GRANT USAGE ON SCHEMA public TO ${readerRole};`);
92
+ for (const table of tables) {
93
+ // Validate table identifier (schema.table form allowed)
94
+ if (!/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(table)) {
95
+ throw new Error(`sharedDb: invalid table identifier ${JSON.stringify(table)}`);
96
+ }
97
+ grants.push(`GRANT SELECT ON ${table} TO ${readerRole};`);
98
+ }
99
+ stanzas.push(`-- bridge_reader grants for ${s.def.name}\n${grants.join("\n")}\n`);
100
+ }
101
+ }
102
+ return stanzas.join("\n");
103
+ }
104
+ //# sourceMappingURL=sharedDb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharedDb.js","sourceRoot":"","sources":["../src/sharedDb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { GlobalConfig } from "./schema/configYaml.js";
2
+ import type { ServiceState } from "./keycloak.js";
3
+ /** Map of relative-output-path → file contents. */
4
+ export type FileTree = Record<string, string>;
5
+ export interface KubernetesExtras {
6
+ /** When provided, an `/opt/keycloak/data/import/realm-import.json` ConfigMap is emitted. */
7
+ realmImport?: string;
8
+ /**
9
+ * When provided, a `postgres-init` ConfigMap is emitted. The shared postgres
10
+ * StatefulSet mounts it at `/docker-entrypoint-initdb.d`, so Postgres runs
11
+ * every `CREATE USER` / `CREATE DATABASE` stanza on first boot.
12
+ */
13
+ postgresInitSql?: string;
14
+ /**
15
+ * Stringified bridge manifest JSON. When provided, a `bridge-manifest`
16
+ * ConfigMap is emitted under configmaps/. The bridge Deployment mounts it
17
+ * at /app/manifest/bridge-manifest.json.
18
+ */
19
+ bridgeManifestJson?: string;
20
+ /**
21
+ * Per-service icon SVG bodies, keyed by service name (no extension).
22
+ * When non-empty, a `bridge-icons` ConfigMap is emitted with one data key
23
+ * per entry: `<name>.svg`. The bridge Deployment mounts it at /app/icons.
24
+ */
25
+ bridgeIconsByService?: Record<string, string>;
26
+ /** Per-file dedicated-peer manifests. Key is the relative output path (e.g. "manifests/drive-postgres.yaml"). */
27
+ dedicatedPeerManifests?: Record<string, string>;
28
+ }
29
+ export type RenderDockerFn = (states: ServiceState[], global: GlobalConfig, templates: Map<string, string>, extraTopLevel: Record<string, string>, extraFragments?: string[]) => FileTree;
30
+ export type RenderKubernetesFn = (states: ServiceState[], global: GlobalConfig, templates: Map<string, string>, extras: KubernetesExtras, lbAnnotations?: Record<string, string>) => FileTree;
31
+ export interface RenderTargets {
32
+ docker?: RenderDockerFn;
33
+ kubernetes?: RenderKubernetesFn;
34
+ }
35
+ //# sourceMappingURL=target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,mDAAmD;AACnD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,iHAAiH;IACjH,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjD;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,cAAc,CAAC,EAAE,MAAM,EAAE,KACtB,QAAQ,CAAC;AAEd,MAAM,MAAM,kBAAkB,GAAG,CAC/B,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,gBAAgB,EACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACnC,QAAQ,CAAC;AAEd,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC"}
package/dist/target.js ADDED
@@ -0,0 +1,7 @@
1
+ // Target abstraction. The core renderer is target-agnostic — it builds the
2
+ // shared service plan, then delegates emission of artifact files to injected
3
+ // target functions. Concrete renderers live in @gezelligate/{docker,k8s} and
4
+ // are wired in by the caller (cli, studio). Core has no compile-time dep on
5
+ // either renderer package; this is what breaks the workspace cycle.
6
+ export {};
7
+ //# sourceMappingURL=target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"target.js","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,oEAAoE"}
@@ -0,0 +1,12 @@
1
+ {{name}}:
2
+ image: postgres:16-alpine
3
+ restart: unless-stopped
4
+ environment:
5
+ POSTGRES_DB: {{dbName}}
6
+ POSTGRES_USER: {{dbUser}}
7
+ POSTGRES_PASSWORD: {{envref passwordRef}}
8
+ volumes:
9
+ - {{name}}-data:/var/lib/postgresql/data
10
+
11
+ volumes:
12
+ {{name}}-data: