@ogygie/studio 0.5.3 → 0.6.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.
package/dist/server.js CHANGED
@@ -104,6 +104,7 @@ import { z as z3 } from "zod";
104
104
  // ../ogygie-core/src/manifest.schema.ts
105
105
  import { z as z2 } from "zod";
106
106
  var FeatureNameSchema = z2.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "kebab-case required");
107
+ var PackageDependencySchema = z2.string().regex(/^package:[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "expected package:<kebab-case-name>");
107
108
  var HttpRouteSchema = z2.object({
108
109
  method: z2.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
109
110
  path: z2.string().startsWith("/api/"),
@@ -128,7 +129,9 @@ var FeatureManifestSchema = z2.object({
128
129
  dbTables: z2.array(DbTableDeclarationSchema).describe(
129
130
  "Drizzle tables owned by this feature (one table = one owner). String = tenant-scoped; { name, shared: true } = shared referential"
130
131
  ),
131
- dependsOn: z2.array(FeatureNameSchema).describe("Features imported through their index.ts \u2014 nothing else"),
132
+ dependsOn: z2.array(z2.union([FeatureNameSchema, PackageDependencySchema])).describe(
133
+ "Features imported through their index.ts, and product packages as package:<name> \u2014 nothing else"
134
+ ),
132
135
  status: z2.enum(["skeleton", "in-progress", "delivered"])
133
136
  });
134
137
  var OgygieVersionSchema = z2.string().regex(/^\d+\.\d+\.\d+$/, "expected a plain x.y.z version");
@@ -156,6 +159,14 @@ var VocabularyEntrySchema = z2.object({
156
159
  var ProductVocabularySchema = z2.object({
157
160
  entries: z2.array(VocabularyEntrySchema)
158
161
  });
162
+ var PackageManifestSchema = z2.object({
163
+ name: FeatureNameSchema,
164
+ path: z2.string().describe("Folder relative to the repo root, e.g. packages/solver"),
165
+ intent: z2.string().min(20).max(300).describe("The package's 'why' in 1-3 sentences"),
166
+ spec: z2.string().nullable(),
167
+ dependsOn: z2.array(PackageDependencySchema).describe("Other PRODUCT packages, as package:<name> \u2014 never a feature: a package that depended on one would live under an app, and would be a feature"),
168
+ status: z2.enum(["skeleton", "in-progress", "delivered"])
169
+ });
159
170
  var OgygieManifestSchema = z2.object({
160
171
  version: z2.literal(1),
161
172
  product: z2.object({
@@ -186,6 +197,7 @@ var OgygieManifestSchema = z2.object({
186
197
  })
187
198
  ),
188
199
  features: z2.array(FeatureManifestSchema),
200
+ packages: z2.array(PackageManifestSchema).optional().describe("Product code living outside every app \u2014 libraries, engines. Absent = the project has none"),
189
201
  vocabulary: ProductVocabularySchema.optional().describe(
190
202
  "The product's closed vocabulary \u2014 declared here, enforced by `ogygie verify-vocabulary`. Absent = gate silent"
191
203
  ),
@@ -195,6 +207,44 @@ var OgygieManifestSchema = z2.object({
195
207
  specsFolder: z2.literal("specs/")
196
208
  })
197
209
  }).superRefine((manifest, context) => {
210
+ const featureNames = new Set(manifest.features.map((feature) => feature.name));
211
+ const packageNames = /* @__PURE__ */ new Set();
212
+ (manifest.packages ?? []).forEach((packageEntry, index) => {
213
+ if (featureNames.has(packageEntry.name)) {
214
+ context.addIssue({
215
+ code: "custom",
216
+ path: ["packages", index, "name"],
217
+ message: `"${packageEntry.name}" is already the name of a feature \u2014 one name designates one thing`
218
+ });
219
+ }
220
+ if (packageNames.has(packageEntry.name)) {
221
+ context.addIssue({
222
+ code: "custom",
223
+ path: ["packages", index, "name"],
224
+ message: `package "${packageEntry.name}" is declared twice`
225
+ });
226
+ }
227
+ packageNames.add(packageEntry.name);
228
+ });
229
+ const checkPackageDependencies = (dependsOn, path) => {
230
+ dependsOn.forEach((dependency, dependencyIndex) => {
231
+ if (!dependency.startsWith("package:")) return;
232
+ const target = dependency.slice("package:".length);
233
+ if (!packageNames.has(target)) {
234
+ context.addIssue({
235
+ code: "custom",
236
+ path: [...path, dependencyIndex],
237
+ message: `dependency "${dependency}" matches no declared package`
238
+ });
239
+ }
240
+ });
241
+ };
242
+ manifest.features.forEach((feature, index) => {
243
+ checkPackageDependencies(feature.dependsOn, ["features", index, "dependsOn"]);
244
+ });
245
+ (manifest.packages ?? []).forEach((packageEntry, index) => {
246
+ checkPackageDependencies(packageEntry.dependsOn, ["packages", index, "dependsOn"]);
247
+ });
198
248
  const canonicalOwners = /* @__PURE__ */ new Map();
199
249
  const aliasOwners = /* @__PURE__ */ new Map();
200
250
  (manifest.vocabulary?.entries ?? []).forEach((entry, index) => {