@rhino-dev/rhino-nestjs 4.8.1 → 4.9.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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Binds the arguments a client sent in the bracket query form
3
+ * (`?scope[name][param]=value`, `?attributes[name][param]=value`) to the
4
+ * parameters a model declared.
5
+ *
6
+ * The algorithm is shared by named scopes and computed attributes so the two
7
+ * features cannot drift. The only thing that differs is `subject` — the noun
8
+ * every error message starts with ("Scope", "Computed attribute") — so each
9
+ * feature keeps its own wording while the behavior stays identical.
10
+ *
11
+ * This is a LEAF module: it imports `RhinoException` and nothing else. It must
12
+ * never import a service, or `rhino.config` would pull a service graph in
13
+ * through it (the `DEFAULT_MAX_SCOPES_PER_REQUEST` cycle).
14
+ *
15
+ * Nothing here decides whether a name may be used: callers MUST run the
16
+ * declared-check and the policy-check BEFORE binding, so an argument error can
17
+ * only ever be seen for a name the caller was already allowed to use.
18
+ */
19
+ export interface BindArgumentsOptions {
20
+ /** Noun for error messages: 'Scope', 'Computed attribute'. */
21
+ subject: string;
22
+ /** The name the client used, echoed in every message. */
23
+ name: string;
24
+ /** Declared parameter names, in declared order. */
25
+ params?: string[];
26
+ /** Declared parameters the client may omit. */
27
+ optional?: string[];
28
+ /** Whatever the query string produced for the bracket key. */
29
+ raw: any;
30
+ }
31
+ /**
32
+ * Bind one name's raw value to a named-argument object.
33
+ *
34
+ * Unlike the PHP and Ruby binders — whose callables take positional arguments —
35
+ * the result here is keyed by parameter name, because that is how it reaches a
36
+ * scope (`ctx.args`) or a computed attribute (`ctx.args` / the third parameter).
37
+ * An omitted optional parameter is simply absent from the object.
38
+ */
39
+ export declare function bindArguments(options: BindArgumentsOptions): Record<string, any>;
40
+ /**
41
+ * Query-string values always arrive as strings; hand callables real booleans so
42
+ * a check cannot be fooled by the string "false". Applies to argument VALUES
43
+ * only, never to names.
44
+ */
45
+ export declare function coerceArgument(value: any): any;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bindArguments = bindArguments;
4
+ exports.coerceArgument = coerceArgument;
5
+ const rhino_exception_1 = require("../errors/rhino-exception");
6
+ /**
7
+ * Bind one name's raw value to a named-argument object.
8
+ *
9
+ * Unlike the PHP and Ruby binders — whose callables take positional arguments —
10
+ * the result here is keyed by parameter name, because that is how it reaches a
11
+ * scope (`ctx.args`) or a computed attribute (`ctx.args` / the third parameter).
12
+ * An omitted optional parameter is simply absent from the object.
13
+ */
14
+ function bindArguments(options) {
15
+ const { subject, name, raw } = options;
16
+ const params = Array.isArray(options.params) ? options.params : [];
17
+ const optional = Array.isArray(options.optional) ? options.optional : [];
18
+ let given;
19
+ if (raw == null || raw === '') {
20
+ // `?attributes[revenue]=` — no arguments. A name with required parameters
21
+ // still fails below, naming them.
22
+ given = {};
23
+ }
24
+ else if (Array.isArray(raw)) {
25
+ // A positional list (`?attributes[revenue][]=a`) names nothing. This also
26
+ // catches a repeated key, which `qs` turns into an array.
27
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' requires named parameters`);
28
+ }
29
+ else if (typeof raw !== 'object') {
30
+ if (params.length === 0) {
31
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' does not accept arguments`);
32
+ }
33
+ // A bare value binds to the single declared parameter. Two parameters can
34
+ // never be guessed at from one value.
35
+ if (params.length > 1) {
36
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' requires named parameters`);
37
+ }
38
+ given = { [params[0]]: raw };
39
+ }
40
+ else {
41
+ if (params.length === 0) {
42
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' does not accept arguments`);
43
+ }
44
+ given = { ...raw };
45
+ }
46
+ for (const key of Object.keys(given)) {
47
+ // Own-key lookup: `constructor` / `__proto__` must never resolve to a
48
+ // prototype member, whatever query parser fed us.
49
+ if (!params.includes(key)) {
50
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' does not accept parameter '${key}'`);
51
+ }
52
+ if (given[key] !== null && typeof given[key] === 'object') {
53
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' requires named parameters`);
54
+ }
55
+ }
56
+ const args = {};
57
+ for (const param of params) {
58
+ if (Object.prototype.hasOwnProperty.call(given, param)) {
59
+ args[param] = coerceArgument(given[param]);
60
+ continue;
61
+ }
62
+ if (!optional.includes(param)) {
63
+ throw rhino_exception_1.RhinoException.forbidden(`${subject} '${name}' requires parameter '${param}'`);
64
+ }
65
+ }
66
+ return args;
67
+ }
68
+ /**
69
+ * Query-string values always arrive as strings; hand callables real booleans so
70
+ * a check cannot be fooled by the string "false". Applies to argument VALUES
71
+ * only, never to names.
72
+ */
73
+ function coerceArgument(value) {
74
+ if (typeof value !== 'string')
75
+ return value;
76
+ const lowered = value.toLowerCase();
77
+ if (lowered === 'true')
78
+ return true;
79
+ if (lowered === 'false')
80
+ return false;
81
+ return value;
82
+ }
83
+ //# sourceMappingURL=argument-binder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"argument-binder.js","sourceRoot":"","sources":["../../src/utils/argument-binder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Declaration handling for computed attributes.
3
+ *
4
+ * A declared value is an EXTENDED SPEC if and only if it is a plain object
5
+ * carrying at least one of `params`, `optionalParams` or `using`. Anything else
6
+ * — a function, a scalar, an array, an object without those keys — is a LEGACY
7
+ * declaration and behaves exactly as it does today.
8
+ *
9
+ * Unlike named scopes, there is deliberately no string or array shorthand: a
10
+ * declared value that is not a function is a *literal* today, and adopting the
11
+ * scope shorthands would silently reinterpret shipped declarations.
12
+ *
13
+ * LEAF module: no service imports (see utils/argument-binder).
14
+ */
15
+ export interface NormalizedComputedSpec {
16
+ params: string[];
17
+ optional: string[];
18
+ /** The callable, or the literal value for a legacy declaration. */
19
+ using: any;
20
+ }
21
+ /** Whether a declared value is an extended spec. */
22
+ export declare function isComputedAttributeSpec(value: any): boolean;
23
+ /** Normalize one declared value into `{ params, optional, using }`. */
24
+ export declare function normalizeComputedAttribute(value: any): NormalizedComputedSpec;
25
+ /**
26
+ * Look a declaration up by name with an OWN-key check, so `constructor` or
27
+ * `__proto__` can never resolve to a prototype member. Returns undefined when
28
+ * the name is not declared.
29
+ */
30
+ export declare function lookupComputedAttribute(declared: Record<string, any> | undefined, name: string): NormalizedComputedSpec | undefined;
31
+ /**
32
+ * Whether the attribute declares at least one parameter the client MUST supply.
33
+ * Such attributes are skipped — never 403'd — when no selection was made (a
34
+ * bare `GET /computed`) and when a direct serializer call passes no arguments.
35
+ */
36
+ export declare function computedAttributeRequiresArguments(spec: NormalizedComputedSpec): boolean;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isComputedAttributeSpec = isComputedAttributeSpec;
4
+ exports.normalizeComputedAttribute = normalizeComputedAttribute;
5
+ exports.lookupComputedAttribute = lookupComputedAttribute;
6
+ exports.computedAttributeRequiresArguments = computedAttributeRequiresArguments;
7
+ const SPEC_KEYS = ['params', 'optionalParams', 'using'];
8
+ /** Whether a declared value is an extended spec. */
9
+ function isComputedAttributeSpec(value) {
10
+ if (value == null || typeof value !== 'object' || Array.isArray(value))
11
+ return false;
12
+ return SPEC_KEYS.some((key) => Object.prototype.hasOwnProperty.call(value, key));
13
+ }
14
+ /** Normalize one declared value into `{ params, optional, using }`. */
15
+ function normalizeComputedAttribute(value) {
16
+ if (!isComputedAttributeSpec(value)) {
17
+ return { params: [], optional: [], using: value };
18
+ }
19
+ const params = Array.isArray(value.params) ? value.params.map(String) : [];
20
+ const optionalRaw = Array.isArray(value.optionalParams)
21
+ ? value.optionalParams.map(String)
22
+ : [];
23
+ return {
24
+ params,
25
+ // An `optionalParams` entry that is not a declared parameter is meaningless.
26
+ optional: optionalRaw.filter((name) => params.includes(name)),
27
+ using: value.using,
28
+ };
29
+ }
30
+ /**
31
+ * Look a declaration up by name with an OWN-key check, so `constructor` or
32
+ * `__proto__` can never resolve to a prototype member. Returns undefined when
33
+ * the name is not declared.
34
+ */
35
+ function lookupComputedAttribute(declared, name) {
36
+ if (!declared || typeof name !== 'string')
37
+ return undefined;
38
+ if (!Object.prototype.hasOwnProperty.call(declared, name))
39
+ return undefined;
40
+ return normalizeComputedAttribute(declared[name]);
41
+ }
42
+ /**
43
+ * Whether the attribute declares at least one parameter the client MUST supply.
44
+ * Such attributes are skipped — never 403'd — when no selection was made (a
45
+ * bare `GET /computed`) and when a direct serializer call passes no arguments.
46
+ */
47
+ function computedAttributeRequiresArguments(spec) {
48
+ return spec.params.some((param) => !spec.optional.includes(param));
49
+ }
50
+ //# sourceMappingURL=computed-attribute-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computed-attribute-spec.js","sourceRoot":"","sources":["../../src/utils/computed-attribute-spec.ts"],"names":[],"mappings":";;AAwBA,0DAGC;AAGD,gEAgBC;AAOD,0DAOC;AAOD,gFAEC;AAhDD,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAU,CAAC;AAEjE,oDAAoD;AACpD,SAAgB,uBAAuB,CAAC,KAAU;IAChD,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrF,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,uEAAuE;AACvE,SAAgB,0BAA0B,CAAC,KAAU;IACnD,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;QACrD,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC;QAClC,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;QACL,MAAM;QACN,6EAA6E;QAC7E,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACrE,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CACrC,QAAyC,EACzC,IAAY;IAEZ,IAAI,CAAC,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5E,OAAO,0BAA0B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,SAAgB,kCAAkC,CAAC,IAA4B;IAC7E,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AACrE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhino-dev/rhino-nestjs",
3
- "version": "4.8.1",
3
+ "version": "4.9.0",
4
4
  "description": "Rhino for NestJS — auto-generated REST APIs from model definitions.",
5
5
  "author": "Bruno Cipolla <bruno@codalio.com>",
6
6
  "license": "MIT",