@voxgig/sdkgen 3.4.1 → 3.4.2

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.
Files changed (58) hide show
  1. package/package.json +1 -1
  2. package/project/.sdk/src/cmp/c/Config_c.ts +1 -1
  3. package/project/.sdk/src/cmp/c/utility_c.ts +57 -6
  4. package/project/.sdk/src/cmp/clojure/Config_clojure.ts +1 -1
  5. package/project/.sdk/src/cmp/clojure/utility_clojure.ts +57 -6
  6. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +1 -1
  7. package/project/.sdk/src/cmp/cpp/utility_cpp.ts +57 -6
  8. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +1 -1
  9. package/project/.sdk/src/cmp/csharp/utility_csharp.ts +57 -6
  10. package/project/.sdk/src/cmp/dart/Config_dart.ts +1 -1
  11. package/project/.sdk/src/cmp/dart/utility_dart.ts +57 -6
  12. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +1 -1
  13. package/project/.sdk/src/cmp/elixir/utility_elixir.ts +57 -6
  14. package/project/.sdk/src/cmp/go/Config_go.ts +1 -1
  15. package/project/.sdk/src/cmp/go/utility_go.ts +57 -6
  16. package/project/.sdk/src/cmp/haskell/Config_haskell.ts +1 -1
  17. package/project/.sdk/src/cmp/haskell/utility_haskell.ts +57 -6
  18. package/project/.sdk/src/cmp/java/Config_java.ts +1 -1
  19. package/project/.sdk/src/cmp/java/utility_java.ts +57 -6
  20. package/project/.sdk/src/cmp/js/Config_js.ts +1 -1
  21. package/project/.sdk/src/cmp/js/utility_js.ts +57 -6
  22. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +1 -1
  23. package/project/.sdk/src/cmp/kotlin/utility_kotlin.ts +57 -6
  24. package/project/.sdk/src/cmp/lean/Config_lean.ts +1 -1
  25. package/project/.sdk/src/cmp/lean/utility_lean.ts +57 -6
  26. package/project/.sdk/src/cmp/lua/Config_lua.ts +1 -1
  27. package/project/.sdk/src/cmp/lua/utility_lua.ts +57 -6
  28. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +1 -1
  29. package/project/.sdk/src/cmp/ocaml/utility_ocaml.ts +57 -6
  30. package/project/.sdk/src/cmp/perl/Config_perl.ts +1 -1
  31. package/project/.sdk/src/cmp/perl/utility_perl.ts +57 -6
  32. package/project/.sdk/src/cmp/php/Config_php.ts +1 -1
  33. package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +22 -0
  34. package/project/.sdk/src/cmp/php/utility_php.ts +57 -6
  35. package/project/.sdk/src/cmp/py/Config_py.ts +1 -1
  36. package/project/.sdk/src/cmp/py/utility_py.ts +57 -6
  37. package/project/.sdk/src/cmp/rb/Config_rb.ts +1 -1
  38. package/project/.sdk/src/cmp/rb/utility_rb.ts +57 -6
  39. package/project/.sdk/src/cmp/rust/Config_rust.ts +1 -1
  40. package/project/.sdk/src/cmp/rust/utility_rust.ts +57 -6
  41. package/project/.sdk/src/cmp/scala/Config_scala.ts +1 -1
  42. package/project/.sdk/src/cmp/scala/utility_scala.ts +57 -6
  43. package/project/.sdk/src/cmp/swift/Config_swift.ts +1 -1
  44. package/project/.sdk/src/cmp/swift/utility_swift.ts +57 -6
  45. package/project/.sdk/src/cmp/ts/Config_ts.ts +1 -1
  46. package/project/.sdk/src/cmp/ts/utility_ts.ts +57 -6
  47. package/project/.sdk/src/cmp/zig/Config_zig.ts +1 -1
  48. package/project/.sdk/src/cmp/zig/utility_zig.ts +57 -6
  49. package/project/.sdk/tm/c/core/point.c +2 -1
  50. package/project/.sdk/tm/csharp/core/Point.cs +3 -1
  51. package/project/.sdk/tm/dart/lib/Point.dart +2 -1
  52. package/project/.sdk/tm/java/core/Point.java +3 -1
  53. package/project/.sdk/tm/js/src/Point.js +3 -1
  54. package/project/.sdk/tm/kotlin/core/Point.kt +3 -1
  55. package/project/.sdk/tm/scala/core/Point.scala +2 -1
  56. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Point.swift +3 -1
  57. package/project/.sdk/tm/ts/src/Point.ts +3 -1
  58. package/project/.sdk/tm/zig/core/point.zig +2 -1
@@ -73,7 +73,7 @@ const Config = cmp(async function Config(props: any) {
73
73
  name: n.name,
74
74
  op: n.op,
75
75
  relations: n.relations,
76
- }), a), {})
76
+ }, true), a), {})
77
77
 
78
78
  const config = {
79
79
  main: { name: model.const.Name },
@@ -68,13 +68,64 @@ function javaVarName(name: string): string {
68
68
 
69
69
 
70
70
  // Strip model bookkeeping keys (ending in $) from a config subtree.
71
- function cleanModel(o: any) {
72
- return walk(clone(o), (k: any, v: any, p: any) => {
73
- if (null != k && k.endsWith('$')) {
74
- delete p[k]
71
+ // Emission-time normalisation of a model subtree (L0).
72
+ //
73
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
74
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
75
+ // default the runtime already assumes when the key is absent, which is pure
76
+ // payload — see CONFIG_DEFAULT.
77
+ //
78
+ // Rebuilds the tree rather than mutating during a walk. The previous
79
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
80
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
81
+ // the delete was undone on the way out and the helper silently did nothing.
82
+ // Returning `undefined` from the callback does not fix it either: setprop
83
+ // stores undefined rather than removing the key, which then emits as a null.
84
+ //
85
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
86
+ // `active` means something different in feature config, where absent reads as
87
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
88
+ // disable the feature.
89
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
90
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
91
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
92
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
93
+ const MODEL_META = ['index$', 'key$', 'val$']
94
+
95
+ // Keys whose value IS the default the runtime already assumes when the key is
96
+ // absent, so emitting them is pure payload.
97
+ const CONFIG_DEFAULT: Record<string, any> = {
98
+ active: true,
99
+ req: false,
100
+ reqd: false,
101
+ }
102
+
103
+ // Subtrees carrying user payload rather than schema. An active:true inside an
104
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
105
+ // keys and everything below them is passed through untouched.
106
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
107
+
108
+ function cleanModel(o: any, dropDefaults?: boolean): any {
109
+ const prune = (node: any, defaults: boolean): any => {
110
+ if (Array.isArray(node)) {
111
+ return node.map((n: any) => prune(n, defaults))
112
+ }
113
+ if (null != node && 'object' === typeof node) {
114
+ const out: any = {}
115
+ for (const k of Object.keys(node)) {
116
+ if (MODEL_META.includes(k)) {
117
+ continue
118
+ }
119
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
120
+ continue
121
+ }
122
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
123
+ }
124
+ return out
75
125
  }
76
- return v
77
- })
126
+ return node
127
+ }
128
+ return prune(o, true === dropDefaults)
78
129
  }
79
130
 
80
131
 
@@ -104,7 +104,7 @@ const Config = cmp(async function Config(props: any) {
104
104
  name: n.name,
105
105
  op: n.op,
106
106
  relations: n.relations,
107
- }), a), {}), { margin: 2 }).trim(),
107
+ }, true), a), {}), { margin: 2 }).trim(),
108
108
  }
109
109
  })
110
110
  })
@@ -102,13 +102,64 @@ function formatJson(obj: any, flags?: { line?: boolean, margin?: number }): stri
102
102
  }
103
103
 
104
104
 
105
- function clean(o: any) {
106
- return walk(clone(o), (k: any, v: any, p: any) => {
107
- if (null != k && k.endsWith('$')) {
108
- delete p[k]
105
+ // Emission-time normalisation of a model subtree (L0).
106
+ //
107
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
108
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
109
+ // default the runtime already assumes when the key is absent, which is pure
110
+ // payload — see CONFIG_DEFAULT.
111
+ //
112
+ // Rebuilds the tree rather than mutating during a walk. The previous
113
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
114
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
115
+ // the delete was undone on the way out and the helper silently did nothing.
116
+ // Returning `undefined` from the callback does not fix it either: setprop
117
+ // stores undefined rather than removing the key, which then emits as a null.
118
+ //
119
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
120
+ // `active` means something different in feature config, where absent reads as
121
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
122
+ // disable the feature.
123
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
124
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
125
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
126
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
127
+ const MODEL_META = ['index$', 'key$', 'val$']
128
+
129
+ // Keys whose value IS the default the runtime already assumes when the key is
130
+ // absent, so emitting them is pure payload.
131
+ const CONFIG_DEFAULT: Record<string, any> = {
132
+ active: true,
133
+ req: false,
134
+ reqd: false,
135
+ }
136
+
137
+ // Subtrees carrying user payload rather than schema. An active:true inside an
138
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
139
+ // keys and everything below them is passed through untouched.
140
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
141
+
142
+ function clean(o: any, dropDefaults?: boolean): any {
143
+ const prune = (node: any, defaults: boolean): any => {
144
+ if (Array.isArray(node)) {
145
+ return node.map((n: any) => prune(n, defaults))
109
146
  }
110
- return v
111
- })
147
+ if (null != node && 'object' === typeof node) {
148
+ const out: any = {}
149
+ for (const k of Object.keys(node)) {
150
+ if (MODEL_META.includes(k)) {
151
+ continue
152
+ }
153
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
154
+ continue
155
+ }
156
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
157
+ }
158
+ return out
159
+ }
160
+ return node
161
+ }
162
+ return prune(o, true === dropDefaults)
112
163
  }
113
164
 
114
165
 
@@ -70,7 +70,7 @@ const Config = cmp(async function Config(props: any) {
70
70
  name: n.name,
71
71
  op: n.op,
72
72
  relations: n.relations,
73
- }), a), {})
73
+ }, true), a), {})
74
74
 
75
75
  const config = {
76
76
  main: { name: model.const.Name },
@@ -64,13 +64,64 @@ function kotlinVarName(name: string): string {
64
64
 
65
65
 
66
66
  // Strip model bookkeeping keys (ending in $) from a config subtree.
67
- function cleanModel(o: any) {
68
- return walk(clone(o), (k: any, v: any, p: any) => {
69
- if (null != k && k.endsWith('$')) {
70
- delete p[k]
67
+ // Emission-time normalisation of a model subtree (L0).
68
+ //
69
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
70
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
71
+ // default the runtime already assumes when the key is absent, which is pure
72
+ // payload — see CONFIG_DEFAULT.
73
+ //
74
+ // Rebuilds the tree rather than mutating during a walk. The previous
75
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
76
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
77
+ // the delete was undone on the way out and the helper silently did nothing.
78
+ // Returning `undefined` from the callback does not fix it either: setprop
79
+ // stores undefined rather than removing the key, which then emits as a null.
80
+ //
81
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
82
+ // `active` means something different in feature config, where absent reads as
83
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
84
+ // disable the feature.
85
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
86
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
87
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
88
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
89
+ const MODEL_META = ['index$', 'key$', 'val$']
90
+
91
+ // Keys whose value IS the default the runtime already assumes when the key is
92
+ // absent, so emitting them is pure payload.
93
+ const CONFIG_DEFAULT: Record<string, any> = {
94
+ active: true,
95
+ req: false,
96
+ reqd: false,
97
+ }
98
+
99
+ // Subtrees carrying user payload rather than schema. An active:true inside an
100
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
101
+ // keys and everything below them is passed through untouched.
102
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
103
+
104
+ function cleanModel(o: any, dropDefaults?: boolean): any {
105
+ const prune = (node: any, defaults: boolean): any => {
106
+ if (Array.isArray(node)) {
107
+ return node.map((n: any) => prune(n, defaults))
108
+ }
109
+ if (null != node && 'object' === typeof node) {
110
+ const out: any = {}
111
+ for (const k of Object.keys(node)) {
112
+ if (MODEL_META.includes(k)) {
113
+ continue
114
+ }
115
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
116
+ continue
117
+ }
118
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
119
+ }
120
+ return out
71
121
  }
72
- return v
73
- })
122
+ return node
123
+ }
124
+ return prune(o, true === dropDefaults)
74
125
  }
75
126
 
76
127
 
@@ -59,7 +59,7 @@ const Config = cmp(async function Config(props: any) {
59
59
  name: n.name,
60
60
  op: n.op,
61
61
  relations: n.relations,
62
- }), a), {})
62
+ }, true), a), {})
63
63
 
64
64
  const config = {
65
65
  main: { name: model.const.Name },
@@ -59,13 +59,64 @@ function pkgName(model: any): string {
59
59
 
60
60
 
61
61
  // Remove `$`-suffixed model annotation keys (so the embedded config is clean).
62
- function clean(o: any) {
63
- return walk(clone(o), (k: any, v: any, p: any) => {
64
- if (null != k && k.endsWith('$')) {
65
- delete p[k]
62
+ // Emission-time normalisation of a model subtree (L0).
63
+ //
64
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
65
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
66
+ // default the runtime already assumes when the key is absent, which is pure
67
+ // payload — see CONFIG_DEFAULT.
68
+ //
69
+ // Rebuilds the tree rather than mutating during a walk. The previous
70
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
71
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
72
+ // the delete was undone on the way out and the helper silently did nothing.
73
+ // Returning `undefined` from the callback does not fix it either: setprop
74
+ // stores undefined rather than removing the key, which then emits as a null.
75
+ //
76
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
77
+ // `active` means something different in feature config, where absent reads as
78
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
79
+ // disable the feature.
80
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
81
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
82
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
83
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
84
+ const MODEL_META = ['index$', 'key$', 'val$']
85
+
86
+ // Keys whose value IS the default the runtime already assumes when the key is
87
+ // absent, so emitting them is pure payload.
88
+ const CONFIG_DEFAULT: Record<string, any> = {
89
+ active: true,
90
+ req: false,
91
+ reqd: false,
92
+ }
93
+
94
+ // Subtrees carrying user payload rather than schema. An active:true inside an
95
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
96
+ // keys and everything below them is passed through untouched.
97
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
98
+
99
+ function clean(o: any, dropDefaults?: boolean): any {
100
+ const prune = (node: any, defaults: boolean): any => {
101
+ if (Array.isArray(node)) {
102
+ return node.map((n: any) => prune(n, defaults))
103
+ }
104
+ if (null != node && 'object' === typeof node) {
105
+ const out: any = {}
106
+ for (const k of Object.keys(node)) {
107
+ if (MODEL_META.includes(k)) {
108
+ continue
109
+ }
110
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
111
+ continue
112
+ }
113
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
114
+ }
115
+ return out
66
116
  }
67
- return v
68
- })
117
+ return node
118
+ }
119
+ return prune(o, true === dropDefaults)
69
120
  }
70
121
 
71
122
 
@@ -102,7 +102,7 @@ ${serverBlock}${authBlock} headers = ${formatLuaTable(headers, 3)},
102
102
  name: n.name,
103
103
  op: n.op,
104
104
  relations: n.relations,
105
- }), a), {}), 2)},
105
+ }, true), a), {}), 2)},
106
106
  }
107
107
  end
108
108
 
@@ -66,13 +66,64 @@ function formatLuaValue(val: any, indent: number = 0): string {
66
66
  }
67
67
 
68
68
 
69
- function clean(o: any) {
70
- return walk(clone(o), (k: any, v: any, p: any) => {
71
- if (null != k && k.endsWith('$')) {
72
- delete p[k]
69
+ // Emission-time normalisation of a model subtree (L0).
70
+ //
71
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
72
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
73
+ // default the runtime already assumes when the key is absent, which is pure
74
+ // payload — see CONFIG_DEFAULT.
75
+ //
76
+ // Rebuilds the tree rather than mutating during a walk. The previous
77
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
78
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
79
+ // the delete was undone on the way out and the helper silently did nothing.
80
+ // Returning `undefined` from the callback does not fix it either: setprop
81
+ // stores undefined rather than removing the key, which then emits as a null.
82
+ //
83
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
84
+ // `active` means something different in feature config, where absent reads as
85
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
86
+ // disable the feature.
87
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
88
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
89
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
90
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
91
+ const MODEL_META = ['index$', 'key$', 'val$']
92
+
93
+ // Keys whose value IS the default the runtime already assumes when the key is
94
+ // absent, so emitting them is pure payload.
95
+ const CONFIG_DEFAULT: Record<string, any> = {
96
+ active: true,
97
+ req: false,
98
+ reqd: false,
99
+ }
100
+
101
+ // Subtrees carrying user payload rather than schema. An active:true inside an
102
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
103
+ // keys and everything below them is passed through untouched.
104
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
105
+
106
+ function clean(o: any, dropDefaults?: boolean): any {
107
+ const prune = (node: any, defaults: boolean): any => {
108
+ if (Array.isArray(node)) {
109
+ return node.map((n: any) => prune(n, defaults))
73
110
  }
74
- return v
75
- })
111
+ if (null != node && 'object' === typeof node) {
112
+ const out: any = {}
113
+ for (const k of Object.keys(node)) {
114
+ if (MODEL_META.includes(k)) {
115
+ continue
116
+ }
117
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
118
+ continue
119
+ }
120
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
121
+ }
122
+ return out
123
+ }
124
+ return node
125
+ }
126
+ return prune(o, true === dropDefaults)
76
127
  }
77
128
 
78
129
 
@@ -68,7 +68,7 @@ const Config = cmp(async function Config(props: any) {
68
68
  name: n.name,
69
69
  op: n.op,
70
70
  relations: n.relations,
71
- }), a), {})
71
+ }, true), a), {})
72
72
 
73
73
  const config = {
74
74
  main: { name: model.const.Name },
@@ -109,13 +109,64 @@ function formatOcamlValue(val: any, indent: number = 0): string {
109
109
 
110
110
 
111
111
  // Deep-remove meta keys (`foo$`) from a model subtree (twin of go's clean).
112
- function clean(o: any) {
113
- return walk(clone(o), (k: any, v: any, p: any) => {
114
- if (null != k && String(k).endsWith('$')) {
115
- delete p[k]
112
+ // Emission-time normalisation of a model subtree (L0).
113
+ //
114
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
115
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
116
+ // default the runtime already assumes when the key is absent, which is pure
117
+ // payload — see CONFIG_DEFAULT.
118
+ //
119
+ // Rebuilds the tree rather than mutating during a walk. The previous
120
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
121
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
122
+ // the delete was undone on the way out and the helper silently did nothing.
123
+ // Returning `undefined` from the callback does not fix it either: setprop
124
+ // stores undefined rather than removing the key, which then emits as a null.
125
+ //
126
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
127
+ // `active` means something different in feature config, where absent reads as
128
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
129
+ // disable the feature.
130
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
131
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
132
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
133
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
134
+ const MODEL_META = ['index$', 'key$', 'val$']
135
+
136
+ // Keys whose value IS the default the runtime already assumes when the key is
137
+ // absent, so emitting them is pure payload.
138
+ const CONFIG_DEFAULT: Record<string, any> = {
139
+ active: true,
140
+ req: false,
141
+ reqd: false,
142
+ }
143
+
144
+ // Subtrees carrying user payload rather than schema. An active:true inside an
145
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
146
+ // keys and everything below them is passed through untouched.
147
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
148
+
149
+ function clean(o: any, dropDefaults?: boolean): any {
150
+ const prune = (node: any, defaults: boolean): any => {
151
+ if (Array.isArray(node)) {
152
+ return node.map((n: any) => prune(n, defaults))
116
153
  }
117
- return v
118
- })
154
+ if (null != node && 'object' === typeof node) {
155
+ const out: any = {}
156
+ for (const k of Object.keys(node)) {
157
+ if (MODEL_META.includes(k)) {
158
+ continue
159
+ }
160
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
161
+ continue
162
+ }
163
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
164
+ }
165
+ return out
166
+ }
167
+ return node
168
+ }
169
+ return prune(o, true === dropDefaults)
119
170
  }
120
171
 
121
172
 
@@ -63,7 +63,7 @@ const Config = cmp(async function Config(props: any) {
63
63
  name: ent.name,
64
64
  op: ent.op,
65
65
  relations: ent.relations,
66
- })
66
+ }, true)
67
67
  })
68
68
 
69
69
  const options: any = {
@@ -14,13 +14,64 @@ function projectPath(suffix?: string): string {
14
14
 
15
15
  // Strip model-internal keys (ending in `$`) so generated config JSON only
16
16
  // carries API-shaped data.
17
- function clean(o: any) {
18
- return walk(clone(o), (k: any, v: any, p: any) => {
19
- if (null != k && k.endsWith('$')) {
20
- delete p[k]
17
+ // Emission-time normalisation of a model subtree (L0).
18
+ //
19
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
20
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
21
+ // default the runtime already assumes when the key is absent, which is pure
22
+ // payload — see CONFIG_DEFAULT.
23
+ //
24
+ // Rebuilds the tree rather than mutating during a walk. The previous
25
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
26
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
27
+ // the delete was undone on the way out and the helper silently did nothing.
28
+ // Returning `undefined` from the callback does not fix it either: setprop
29
+ // stores undefined rather than removing the key, which then emits as a null.
30
+ //
31
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
32
+ // `active` means something different in feature config, where absent reads as
33
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
34
+ // disable the feature.
35
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
36
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
37
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
38
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
39
+ const MODEL_META = ['index$', 'key$', 'val$']
40
+
41
+ // Keys whose value IS the default the runtime already assumes when the key is
42
+ // absent, so emitting them is pure payload.
43
+ const CONFIG_DEFAULT: Record<string, any> = {
44
+ active: true,
45
+ req: false,
46
+ reqd: false,
47
+ }
48
+
49
+ // Subtrees carrying user payload rather than schema. An active:true inside an
50
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
51
+ // keys and everything below them is passed through untouched.
52
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
53
+
54
+ function clean(o: any, dropDefaults?: boolean): any {
55
+ const prune = (node: any, defaults: boolean): any => {
56
+ if (Array.isArray(node)) {
57
+ return node.map((n: any) => prune(n, defaults))
58
+ }
59
+ if (null != node && 'object' === typeof node) {
60
+ const out: any = {}
61
+ for (const k of Object.keys(node)) {
62
+ if (MODEL_META.includes(k)) {
63
+ continue
64
+ }
65
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
66
+ continue
67
+ }
68
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
69
+ }
70
+ return out
21
71
  }
22
- return v
23
- })
72
+ return node
73
+ }
74
+ return prune(o, true === dropDefaults)
24
75
  }
25
76
 
26
77
 
@@ -145,7 +145,7 @@ ${serverBlock}${authBlock} "headers" => ${formatPhpArray(headers,
145
145
  name: n.name,
146
146
  op: n.op,
147
147
  relations: n.relations,
148
- }), a), {}), 3)},
148
+ }, true), a), {}), 3)},
149
149
  ];
150
150
  `)
151
151
  }
@@ -17,6 +17,7 @@ class EntyClass
17
17
  private array $_data;
18
18
  private array $_match;
19
19
  private $_entctx;
20
+ private bool $_deleted = false;
20
21
 
21
22
  public function __construct($client, ?array $entopts = null)
22
23
  {
@@ -49,6 +50,27 @@ class EntyClass
49
50
  return $this->_name;
50
51
  }
51
52
 
53
+ /**
54
+ * A `remove` marks the entity deleted. The instance KEEPS the data it
55
+ * held — a caller can still read what was removed — but it is no longer a
56
+ * live record. See AGENTS.md "Entity operations return ENTITIES".
57
+ *
58
+ * The remove path below already called markDeleted(); php was the one
59
+ * target that never declared it (cpp and swift both do), so any SDK whose
60
+ * entities have a `remove` op raised "Call to undefined method
61
+ * <Entity>::markDeleted()" the first time a remove succeeded. Nothing
62
+ * caught it because the fatal only fires when that path actually runs.
63
+ */
64
+ public function markDeleted(): void
65
+ {
66
+ $this->_deleted = true;
67
+ }
68
+
69
+ public function deleted(): bool
70
+ {
71
+ return $this->_deleted;
72
+ }
73
+
52
74
  public function make(): self
53
75
  {
54
76
  $opts = $this->_entopts;