@omnifyjp/omnify 5.8.12 → 5.8.14

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": "@omnifyjp/omnify",
3
- "version": "5.8.12",
3
+ "version": "5.8.14",
4
4
  "description": "Schema-driven code generation for Laravel, TypeScript, and SQL",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,10 +36,10 @@
36
36
  "zod": "^3.24.0"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@omnifyjp/omnify-darwin-arm64": "5.8.12",
40
- "@omnifyjp/omnify-darwin-x64": "5.8.12",
41
- "@omnifyjp/omnify-linux-x64": "5.8.12",
42
- "@omnifyjp/omnify-linux-arm64": "5.8.12",
43
- "@omnifyjp/omnify-win32-x64": "5.8.12"
39
+ "@omnifyjp/omnify-darwin-arm64": "5.8.14",
40
+ "@omnifyjp/omnify-darwin-x64": "5.8.14",
41
+ "@omnifyjp/omnify-linux-x64": "5.8.14",
42
+ "@omnifyjp/omnify-linux-arm64": "5.8.14",
43
+ "@omnifyjp/omnify-win32-x64": "5.8.14"
44
44
  }
45
45
  }
@@ -7,7 +7,25 @@
7
7
  import { SchemaReader } from './schema-reader.js';
8
8
  import type { GeneratedFile, PhpConfig } from './types.js';
9
9
  import type { SchemaDefinition, SingleCondition } from '../types.js';
10
- /** Generate Laravel Policy classes for all schemas that have policies. */
10
+ /** Generate Laravel Policy classes for project-owned visible schemas.
11
+ *
12
+ * Issue #98 v5.8.14: every `kind: object` schema now gets a generated
13
+ * policy base + editable stub by default — Laravel's policy
14
+ * auto-discovery (`App\\Models\\<Name>` → `App\\Policies\\<Name>Policy`)
15
+ * works out of the box without per-project Gate::policy(...) wiring.
16
+ *
17
+ * Skip rules:
18
+ * - `kind: pivot` — pivots have no API surface
19
+ * - `options.policy: false` — explicit per-schema opt-out
20
+ *
21
+ * Two code paths per schema:
22
+ * - Explicit Cedar-style `policies: [...]` → existing rule-based body
23
+ * generator (forbid / permit / when conditions).
24
+ * - No `policies:` declared → standard CRUD scaffolding (viewAny /
25
+ * view / create / update / delete + restore / forceDelete on
26
+ * softDelete). Default body: `return true;`. Project secures by
27
+ * overriding the method in the editable child.
28
+ */
11
29
  export declare function generatePolicies(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
12
30
  /** Convert a single condition to a PHP expression. */
13
31
  export declare function conditionToPhp(cond: SingleCondition, schema: SchemaDefinition, reader: SchemaReader): string;
@@ -24,11 +24,31 @@ const RECORD_ACTIONS = new Set(['view', 'edit', 'delete']);
24
24
  // ============================================================================
25
25
  // Public API
26
26
  // ============================================================================
27
- /** Generate Laravel Policy classes for all schemas that have policies. */
27
+ /** Generate Laravel Policy classes for project-owned visible schemas.
28
+ *
29
+ * Issue #98 v5.8.14: every `kind: object` schema now gets a generated
30
+ * policy base + editable stub by default — Laravel's policy
31
+ * auto-discovery (`App\\Models\\<Name>` → `App\\Policies\\<Name>Policy`)
32
+ * works out of the box without per-project Gate::policy(...) wiring.
33
+ *
34
+ * Skip rules:
35
+ * - `kind: pivot` — pivots have no API surface
36
+ * - `options.policy: false` — explicit per-schema opt-out
37
+ *
38
+ * Two code paths per schema:
39
+ * - Explicit Cedar-style `policies: [...]` → existing rule-based body
40
+ * generator (forbid / permit / when conditions).
41
+ * - No `policies:` declared → standard CRUD scaffolding (viewAny /
42
+ * view / create / update / delete + restore / forceDelete on
43
+ * softDelete). Default body: `return true;`. Project secures by
44
+ * overriding the method in the editable child.
45
+ */
28
46
  export function generatePolicies(reader, config) {
29
47
  const files = [];
30
48
  for (const [name, schema] of Object.entries(reader.getProjectVisibleObjectSchemas())) {
31
- if (!schema.policies || schema.policies.length === 0)
49
+ if (schema.kind === 'pivot')
50
+ continue;
51
+ if (schema.options?.policy === false)
32
52
  continue;
33
53
  files.push(...generateForSchema(name, schema, reader, config));
34
54
  }
@@ -46,31 +66,49 @@ function generateForSchema(name, schema, reader, config) {
46
66
  function generateBasePolicyClass(name, schema, reader, config) {
47
67
  const modelName = toPascalCase(name);
48
68
  const baseNamespace = resolveModularBaseNamespace(config, name, 'Policies', config.policies.baseNamespace);
49
- // Issue #98 v5.8.4: legacy structure groups Models by folder. The
50
- // policy's `use ${modelNamespace}\\${modelName}` import must include
51
- // the schema's group too, otherwise the policy fails to autoload at
52
- // runtime when Laravel resolves it from the gate registration.
69
+ // Issue #98 v5.8.13: policy method signatures (`view(User $user,
70
+ // <Model> $record): bool`) must type-hint the USER-EDITABLE Model
71
+ // class. Same root cause + fix as the v5.8.11 service / controller
72
+ // signature fix — when the user-editable policy child overrides a
73
+ // base method with the editable Model type, PHP signature
74
+ // contravariance fires with a Fatal compile error if the parent
75
+ // typed against the BASE Model. Modular structure: per-Schema
76
+ // isolation already aligns base + editable on one namespace.
53
77
  const modelNamespace = config.structure === 'modular'
54
78
  ? config.models.namespace
55
- : nestByGroup({ path: '', namespace: config.models.namespace }, schema.group).namespace;
56
- const policies = schema.policies;
79
+ : (config.models.userEditableGroupByFolder
80
+ ? nestByGroup({ path: '', namespace: config.models.userEditableNamespace }, schema.group).namespace
81
+ : config.models.userEditableNamespace);
82
+ // Issue #98 v5.8.14: when no Cedar-style `policies:` array is defined,
83
+ // emit the standard CRUD scaffold with `return true;` bodies.
84
+ // The project secures the policy by overriding methods in the
85
+ // editable child (e.g. `return $user->hasPermission('manage.banner');`).
86
+ const policies = schema.policies ?? [];
87
+ const hasExplicitPolicies = policies.length > 0;
57
88
  const hasSoftDelete = schema.options?.softDelete ?? false;
58
- const needsCidrHelper = policiesUseCidr(policies);
59
- // Collect all actions that have policies
60
- const expandedPolicies = expandWildcards(policies);
89
+ const needsCidrHelper = hasExplicitPolicies ? policiesUseCidr(policies) : false;
90
+ // Collect all actions that have policies (only relevant for the
91
+ // explicit-policy path; standard CRUD doesn't need wildcard expansion).
92
+ const expandedPolicies = hasExplicitPolicies ? expandWildcards(policies) : policies;
61
93
  // Build methods
62
94
  const methods = [];
63
95
  for (const action of ALL_ACTIONS) {
64
96
  const methodName = ACTION_METHOD_MAP[action];
65
97
  const hasRecord = RECORD_ACTIONS.has(action);
66
- const body = generateMethodBody(action, expandedPolicies, schema, reader, hasRecord);
98
+ const body = hasExplicitPolicies
99
+ ? generateMethodBody(action, expandedPolicies, schema, reader, hasRecord)
100
+ : ' return true;';
67
101
  methods.push(buildMethod(methodName, modelName, hasRecord, body));
68
102
  }
69
103
  // SoftDelete: restore + forceDelete
70
104
  if (hasSoftDelete) {
71
- const restoreBody = generateMethodBody('delete', expandedPolicies, schema, reader);
105
+ const restoreBody = hasExplicitPolicies
106
+ ? generateMethodBody('delete', expandedPolicies, schema, reader)
107
+ : ' return true;';
72
108
  methods.push(buildMethod('restore', modelName, true, restoreBody));
73
- const forceDeleteBody = generateMethodBody('delete', expandedPolicies, schema, reader);
109
+ const forceDeleteBody = hasExplicitPolicies
110
+ ? generateMethodBody('delete', expandedPolicies, schema, reader)
111
+ : ' return true;';
74
112
  methods.push(buildMethod('forceDelete', modelName, true, forceDeleteBody));
75
113
  }
76
114
  const cidrHelper = needsCidrHelper ? buildCidrHelper() : '';
@@ -208,6 +208,17 @@ export interface SchemaOptions {
208
208
  * generate time).
209
209
  */
210
210
  readonly service?: ServiceOptions | false;
211
+ /**
212
+ * Per-schema opt-out for the auto-generated Policy. Issue #98 v5.8.14:
213
+ * every `kind: object` schema gets a generated policy base + editable
214
+ * stub by default. Set `policy: false` on schemas that should NOT have
215
+ * an authorization policy (translation tables, internal sidecars, etc).
216
+ * Cedar-style ABAC `policies:` array on the root schema definition
217
+ * still drives the generated method bodies when present; otherwise the
218
+ * generator emits a standard 5-method CRUD scaffold with
219
+ * `return true;` bodies for the project to override in the editable.
220
+ */
221
+ readonly policy?: false;
211
222
  /** Schema-level default ordering — generates a global Eloquent scope. Issue #40. */
212
223
  readonly defaultOrder?: readonly OrderByItem[];
213
224
  }