@volter/world-core 3.0.104 → 3.0.105

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.
@@ -211,14 +211,16 @@ export type ResourceDecl = {
211
211
  partial?: string[];
212
212
  };
213
213
  /** fields that count this subject's children (an App's `installations_count`): the live subjects of `of` whose `by`
214
- * holds this one's id (or vendor `parentKey`), only those with `present` set when it is named and without
214
+ * holds this one's id (or vendor `parentKey`), only those with `present` set or `absent` unset when named and without
215
215
  * any matching `unless` field, plus `plus` (a customer's
216
- * `next_invoice_sequence` is one past its numbered invoices). The kernel keeps each whenever a child is written. */
216
+ * `next_invoice_sequence` is one past its numbered invoices). `by` may be a dot path; exact field names take
217
+ * precedence. The kernel keeps each whenever a child is written. */
217
218
  counts?: Record<string, {
218
219
  of: string;
219
220
  by: string;
220
221
  parentKey?: string;
221
222
  present?: string;
223
+ absent?: string;
222
224
  unless?: Record<string, unknown>;
223
225
  plus?: number;
224
226
  }>;
@@ -756,15 +756,18 @@ async function finishWrite(m, call, resource, row, result, operation, params, ro
756
756
  async function keepCounts(m, call, resource, row, params, root, occurredAt) {
757
757
  for (const [parent, decl] of Object.entries(m.resources)) {
758
758
  for (const [field, c] of Object.entries(decl.counts ?? {})) {
759
- if (storedType(m, c.of) !== storedType(m, resource) || row[c.by] === undefined || row[c.by] === null)
759
+ if (storedType(m, c.of) !== storedType(m, resource))
760
760
  continue;
761
- const pid = String(row[c.by]);
761
+ const parentId = parentField(row, c.by);
762
+ if (parentId === undefined || parentId === null)
763
+ continue;
764
+ const pid = String(parentId);
762
765
  const held = stored(m, parent, root).find((r) => String(c.parentKey ? ownFields(r)[c.parentKey] : r.id) === pid);
763
766
  if (!held)
764
767
  continue;
765
768
  const n = stored(m, c.of, root).filter((r) => {
766
769
  const x = r;
767
- return String(x[c.by]) === pid && (c.present === undefined || (x[c.present] !== undefined && x[c.present] !== null)) && Object.entries(c.unless ?? {}).every(([key, value]) => ownFields(r)[key] !== value);
770
+ return String(parentField(x, c.by)) === pid && (c.present === undefined || (x[c.present] !== undefined && x[c.present] !== null)) && (c.absent === undefined || parentField(ownFields(r), c.absent) === undefined || parentField(ownFields(r), c.absent) === null) && Object.entries(c.unless ?? {}).every(([key, value]) => ownFields(r)[key] !== value);
768
771
  }).length + (c.plus ?? 0);
769
772
  if (held[field] !== n)
770
773
  await writeDetailed(m, call, parent, String(held.id), { [field]: n }, `${storedType(m, parent)}.counted`, params, root, occurredAt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world-core",
3
- "version": "3.0.104",
3
+ "version": "3.0.105",
4
4
  "description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
5
5
  "keywords": [
6
6
  "twin",
@@ -194,10 +194,11 @@ export type ResourceDecl = {
194
194
  * substring, case-insensitively */
195
195
  search?: { param: string; exact?: string[]; partial?: string[] };
196
196
  /** fields that count this subject's children (an App's `installations_count`): the live subjects of `of` whose `by`
197
- * holds this one's id (or vendor `parentKey`), only those with `present` set when it is named and without
197
+ * holds this one's id (or vendor `parentKey`), only those with `present` set or `absent` unset when named and without
198
198
  * any matching `unless` field, plus `plus` (a customer's
199
- * `next_invoice_sequence` is one past its numbered invoices). The kernel keeps each whenever a child is written. */
200
- counts?: Record<string, { of: string; by: string; parentKey?: string; present?: string; unless?: Record<string, unknown>; plus?: number }>;
199
+ * `next_invoice_sequence` is one past its numbered invoices). `by` may be a dot path; exact field names take
200
+ * precedence. The kernel keeps each whenever a child is written. */
201
+ counts?: Record<string, { of: string; by: string; parentKey?: string; present?: string; absent?: string; unless?: Record<string, unknown>; plus?: number }>;
201
202
  /** how the vendor's state of this type is read back into a root (the real-system adapters, "Refresh"): the list that
202
203
  * enumerates it (once per parent, under `parent`; `complete: false` when it is not the whole type; `items`, the answer's
203
204
  * path to them, when the spec does not name the list it answers), the one read of a singleton, or `none` with why the
@@ -1233,13 +1234,15 @@ async function finishWrite(m: DerivedManifest, call: DerivedCall, resource: stri
1233
1234
  async function keepCounts(m: DerivedManifest, call: DerivedCall, resource: string, row: Record<string, unknown>, params: Record<string, unknown>, root: string | undefined, occurredAt: string): Promise<void> {
1234
1235
  for (const [parent, decl] of Object.entries(m.resources)) {
1235
1236
  for (const [field, c] of Object.entries(decl.counts ?? {})) {
1236
- if (storedType(m, c.of) !== storedType(m, resource) || row[c.by] === undefined || row[c.by] === null) continue;
1237
- const pid = String(row[c.by]);
1237
+ if (storedType(m, c.of) !== storedType(m, resource)) continue;
1238
+ const parentId = parentField(row, c.by);
1239
+ if (parentId === undefined || parentId === null) continue;
1240
+ const pid = String(parentId);
1238
1241
  const held = stored(m, parent, root).find((r) => String(c.parentKey ? ownFields(r)[c.parentKey] : r.id) === pid) as Record<string, unknown> | undefined;
1239
1242
  if (!held) continue;
1240
1243
  const n = stored(m, c.of, root).filter((r) => {
1241
1244
  const x = r as Record<string, unknown>;
1242
- return String(x[c.by]) === pid && (c.present === undefined || (x[c.present] !== undefined && x[c.present] !== null)) && Object.entries(c.unless ?? {}).every(([key, value]) => ownFields(r)[key] !== value);
1245
+ return String(parentField(x, c.by)) === pid && (c.present === undefined || (x[c.present] !== undefined && x[c.present] !== null)) && (c.absent === undefined || parentField(ownFields(r), c.absent) === undefined || parentField(ownFields(r), c.absent) === null) && Object.entries(c.unless ?? {}).every(([key, value]) => ownFields(r)[key] !== value);
1243
1246
  }).length + (c.plus ?? 0);
1244
1247
  if (held[field] !== n) await writeDetailed(m, call, parent, String(held.id), { [field]: n }, `${storedType(m, parent)}.counted`, params, root, occurredAt);
1245
1248
  }