@cosmicdrift/kumiko-framework 0.12.2 → 0.13.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/CHANGELOG.md +22 -0
- package/package.json +2 -2
- package/src/db/table-builder.ts +5 -0
- package/src/engine/factories.ts +12 -0
- package/src/engine/index.ts +2 -0
- package/src/engine/schema-builder.ts +5 -0
- package/src/engine/types/fields.ts +16 -0
- package/src/engine/types/index.ts +1 -0
- package/src/testing/e2e-generator.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @cosmicdrift/kumiko-framework
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 7f56b2f: **Framework**: add `JsonbFieldDef` + `createJsonbField()` primitive. Schema-less jsonb-Spalte (default `{}`, NOT NULL) für tenant-defined extension-data, AI-inferred metadata, free-form config-blobs. Vs. `embedded` (typed sub-schema): jsonb akzeptiert beliebige keys. Table-builder + schema-builder + e2e-generator alle aktualisiert.
|
|
8
|
+
|
|
9
|
+
**custom-fields-Bundle (B2)**: ergänzt B1 um Custom-Field-VALUES:
|
|
10
|
+
|
|
11
|
+
- `customField.set` + `customField.cleared` Event-Types (auf host-aggregate stream)
|
|
12
|
+
- `set-custom-field` + `clear-custom-field` write-handlers (emit events)
|
|
13
|
+
- `r.extendsRegistrar("customFields")` für consumer opt-in via `useExtension`
|
|
14
|
+
- `customFieldsField()` helper für entity-fields-definition
|
|
15
|
+
- `wireCustomFieldsFor(r, entityName, entityTable)` consumer-side-API registriert:
|
|
16
|
+
- `r.useExtension("customFields", entity)` opt-in marker
|
|
17
|
+
- MultiStreamProjection: customField.set/.cleared/fieldDefinition.deleted → UPDATE entityTable.customFields jsonb (jsonb_set / minus-operator)
|
|
18
|
+
- `r.entityHook("postQuery", entity, ...)` — flatten row.customFields auf API-root (Spec-Promise "indistinguishable von Stammfeldern")
|
|
19
|
+
- `r.searchPayloadExtension(entity, ...)` — customFields-keys flach ins Meilisearch-Index (F3 wiring)
|
|
20
|
+
|
|
21
|
+
**Out-of-B2** (future iterations): cross-scope-conflict (tenant override system fieldKey), cap-counter quota, user-data-rights anonymization, value-validation gegen fieldDefinition.serializedField, system+tenant UNION-read.
|
|
22
|
+
|
|
23
|
+
Part of custom-fields-bundle Sprint Phase B2 (Plan-Doc: kumiko-platform/docs/plans/custom-fields-sprint.md).
|
|
24
|
+
|
|
3
25
|
## 0.12.2
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
"zod": "^4.4.3"
|
|
173
173
|
},
|
|
174
174
|
"devDependencies": {
|
|
175
|
-
"@cosmicdrift/kumiko-dispatcher-live": "0.
|
|
175
|
+
"@cosmicdrift/kumiko-dispatcher-live": "0.13.0",
|
|
176
176
|
"@types/uuid": "^11.0.0",
|
|
177
177
|
"bun-types": "^1.3.13",
|
|
178
178
|
"drizzle-kit": "^0.31.10",
|
package/src/db/table-builder.ts
CHANGED
|
@@ -132,6 +132,11 @@ function fieldToColumns(
|
|
|
132
132
|
// "Feld komplett weglassen können") modelliert das über ein
|
|
133
133
|
// wrapper-feld mit boolean-flag oder discriminierte-union.
|
|
134
134
|
return { [name]: jsonb(snakeName).default({}).notNull() };
|
|
135
|
+
case "jsonb":
|
|
136
|
+
// Free-form jsonb — keys nicht schema-validated. Default `{}`, NOT NULL
|
|
137
|
+
// (analog zu embedded). Use-case: custom-fields-Bundle's host-entity-
|
|
138
|
+
// `customFields`-Spalte (tenant-definierte dynamische keys).
|
|
139
|
+
return { [name]: jsonb(snakeName).default({}).notNull() };
|
|
135
140
|
case "date": {
|
|
136
141
|
// `type:"date"` aliased auf instant() = TIMESTAMPTZ. Echte
|
|
137
142
|
// PlainDate-Migration (PG `date` Spalte, kein TZ) kommt später.
|
package/src/engine/factories.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
FilesFieldDef,
|
|
11
11
|
ImageFieldDef,
|
|
12
12
|
ImagesFieldDef,
|
|
13
|
+
JsonbFieldDef,
|
|
13
14
|
LocatedTimestampFieldDef,
|
|
14
15
|
LongTextFieldDef,
|
|
15
16
|
MoneyFieldDef,
|
|
@@ -159,6 +160,17 @@ export function createEmbeddedField(
|
|
|
159
160
|
};
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
// Free-form jsonb-Spalte — siehe `JsonbFieldDef`-Doku. Schema-less, default
|
|
164
|
+
// `{}`, NOT NULL. Hauptnutzer: custom-fields-Bundle (host-entity's
|
|
165
|
+
// `customFields`-Spalte). Andere valid uses: tenant-config-blobs, AI-
|
|
166
|
+
// inferred-metadata, future tags-arrays.
|
|
167
|
+
export function createJsonbField(overrides?: Partial<Omit<JsonbFieldDef, "type">>): JsonbFieldDef {
|
|
168
|
+
return {
|
|
169
|
+
type: "jsonb",
|
|
170
|
+
...overrides,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
162
174
|
export function createDateField<R extends true | false = false>(
|
|
163
175
|
overrides?: Partial<Omit<DateFieldDef, "type" | "required">> & { required?: R },
|
|
164
176
|
): DateFieldDef & { required: R } {
|
package/src/engine/index.ts
CHANGED
|
@@ -85,6 +85,7 @@ export {
|
|
|
85
85
|
createFilesField,
|
|
86
86
|
createImageField,
|
|
87
87
|
createImagesField,
|
|
88
|
+
createJsonbField,
|
|
88
89
|
createLocatedTimestampField,
|
|
89
90
|
createLongTextField,
|
|
90
91
|
createMoneyField,
|
|
@@ -252,6 +253,7 @@ export type {
|
|
|
252
253
|
JobDefinition,
|
|
253
254
|
JobHandlerFn,
|
|
254
255
|
JobTrigger,
|
|
256
|
+
JsonbFieldDef,
|
|
255
257
|
KumikoEntityTypeMap,
|
|
256
258
|
KumikoEventTypeMap,
|
|
257
259
|
KumikoHandlerPayloadMap,
|
|
@@ -84,6 +84,11 @@ function fieldToZod(field: FieldDefinition, currencies: readonly string[]): z.Zo
|
|
|
84
84
|
}
|
|
85
85
|
return z.object(shape);
|
|
86
86
|
}
|
|
87
|
+
case "jsonb": {
|
|
88
|
+
// Free-form jsonb — keys sind tenant-/runtime-defined. Validation
|
|
89
|
+
// passthrough: any plain object passt durch.
|
|
90
|
+
return z.record(z.string(), z.unknown());
|
|
91
|
+
}
|
|
87
92
|
case "date": {
|
|
88
93
|
return z.string().date();
|
|
89
94
|
}
|
|
@@ -302,6 +302,21 @@ export type EmbeddedFieldDef = {
|
|
|
302
302
|
readonly access?: FieldAccess;
|
|
303
303
|
} & PiiAnnotations;
|
|
304
304
|
|
|
305
|
+
// Free-form jsonb — keys/shape NOT validated at write-time. Use for:
|
|
306
|
+
// - Tenant-defined extension data (custom-fields-bundle uses this for
|
|
307
|
+
// `customFields` on host-entities — keys are dynamic per fieldDefinition)
|
|
308
|
+
// - Configuration-blobs with shape that evolves outside Stammfeld-schema
|
|
309
|
+
// - AI-inferred metadata where shape is provider-dependent
|
|
310
|
+
//
|
|
311
|
+
// Vs. embedded: embedded enforces a typed sub-schema; jsonb accepts any
|
|
312
|
+
// JSON-shaped object. Read-side both map to Postgres `jsonb`. Default `{}`
|
|
313
|
+
// + NOT NULL, identisch zu embedded.
|
|
314
|
+
export type JsonbFieldDef = {
|
|
315
|
+
readonly type: "jsonb";
|
|
316
|
+
readonly sensitive?: boolean;
|
|
317
|
+
readonly access?: FieldAccess;
|
|
318
|
+
} & PiiAnnotations;
|
|
319
|
+
|
|
305
320
|
// Legacy "date" — JS-Date-Object, semantisch unklar (Wall-Clock vs Instant).
|
|
306
321
|
// Für neue Felder bevorzuge:
|
|
307
322
|
// - `timestamp` für UTC-Instant ("wann ist das passiert")
|
|
@@ -429,6 +444,7 @@ export type FieldDefinition =
|
|
|
429
444
|
| MoneyFieldDef
|
|
430
445
|
| ReferenceFieldDef
|
|
431
446
|
| EmbeddedFieldDef
|
|
447
|
+
| JsonbFieldDef
|
|
432
448
|
| DateFieldDef
|
|
433
449
|
| TimestampFieldDef
|
|
434
450
|
| TzFieldDef
|