@everystack/cli 0.4.3 → 0.4.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everystack/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "CLI and OTA updates for Expo apps on everystack",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": "Scalable Technology, Inc. <licensing@scalable.technology>",
@@ -267,9 +267,12 @@ export function compileTableContract(model: ModelDescriptor, opts: CompileOption
267
267
  const anonUsing = sdGuard ?? 'true';
268
268
  policy(`${t}_select_anon`, 'SELECT', ['anon'], anonUsing, null);
269
269
 
270
- let authedUsing = anonUsing;
271
- if (sdGuard && rowPred) authedUsing = `(${sdGuard} OR ${rowPred})`;
272
- else if (rowPred && !sdGuard) authedUsing = rowPred;
270
+ // The owner predicate may only ever WIDEN a public read (the soft-delete OR:
271
+ // owners also see their own deleted rows). It must never REPLACE it — an owner
272
+ // condition on a WRITE ability used to narrow the authenticated SELECT to
273
+ // own-rows-only, so anon saw the whole table and a signed-in user lost it.
274
+ // Without a soft-delete guard, `true OR owner` is just `true`.
275
+ const authedUsing = sdGuard && rowPred ? `(${sdGuard} OR ${rowPred})` : anonUsing;
273
276
  policy(`${t}_select_authenticated`, 'SELECT', ['authenticated'], authedUsing, null);
274
277
  } else if (hasOwnerRead && rowPred) {
275
278
  // owner-scoped read — no anon visibility; you see only the rows you own
@@ -319,11 +319,13 @@ export function compileDerived(models: readonly ModelDescriptor[], derived: read
319
319
 
320
320
  // Triggers declared on models and on views — the deps come free (execute fn; the
321
321
  // declaration-site view when it is itself derived). The function MUST be declared.
322
+ // `?? []` mirrors the first trigger walk above: a descriptor built by an older
323
+ // @everystack/model arrives without the key, and this walk crashed "not iterable".
322
324
  const triggerOwners: Array<{ schema: string; name: string; ownerIdentity: string | null; triggers: TriggerSpec[] }> = [
323
- ...models.map((m) => ({ schema: m.schema, name: m.table, ownerIdentity: null, triggers: m.triggers })),
325
+ ...models.map((m) => ({ schema: m.schema, name: m.table, ownerIdentity: null, triggers: m.triggers ?? [] })),
324
326
  ...derived.filter((d): d is ViewDescriptor => d.kind === 'view').map((v) => {
325
327
  const q = parseQualified(v.name);
326
- return { schema: q.schema, name: q.name, ownerIdentity: `${q.schema}.${q.name}`, triggers: v.triggers };
328
+ return { schema: q.schema, name: q.name, ownerIdentity: `${q.schema}.${q.name}`, triggers: v.triggers ?? [] };
327
329
  }),
328
330
  ];
329
331
  for (const owner of triggerOwners) {