UNPKG

5.95 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
3// Node module: @loopback/context
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.filterByKey = exports.filterByTag = exports.includesTagValue = exports.ANY_TAG_VALUE = exports.isBindingTagFilter = exports.isBindingAddress = void 0;
8/**
9 * Check if an object is a `BindingKey` by duck typing
10 * @param selector Binding selector
11 */
12function isBindingKey(selector) {
13 if (selector == null || typeof selector !== 'object')
14 return false;
15 return (typeof selector.key === 'string' &&
16 typeof selector.deepProperty === 'function');
17}
18/**
19 * Type guard for binding address
20 * @param bindingSelector - Binding key or filter function
21 */
22function isBindingAddress(bindingSelector) {
23 return (typeof bindingSelector !== 'function' &&
24 (typeof bindingSelector === 'string' ||
25 // See https://github.com/loopbackio/loopback-next/issues/4570
26 // `bindingSelector instanceof BindingKey` is not always reliable as the
27 // `@loopback/context` module might be loaded from multiple locations if
28 // `npm install` does not dedupe or there are mixed versions in the tree
29 isBindingKey(bindingSelector)));
30}
31exports.isBindingAddress = isBindingAddress;
32/**
33 * Type guard for BindingTagFilter
34 * @param filter - A BindingFilter function
35 */
36function isBindingTagFilter(filter) {
37 if (filter == null || !('bindingTagPattern' in filter))
38 return false;
39 // eslint-disable-next-line @typescript-eslint/no-explicit-any
40 const tagPattern = filter.bindingTagPattern;
41 return (tagPattern instanceof RegExp ||
42 typeof tagPattern === 'string' ||
43 typeof tagPattern === 'object');
44}
45exports.isBindingTagFilter = isBindingTagFilter;
46/**
47 * A symbol that can be used to match binding tags by name regardless of the
48 * value.
49 *
50 * @example
51 *
52 * The following code matches bindings with tag `{controller: 'A'}` or
53 * `{controller: 'controller'}`. But if the tag name 'controller' does not
54 * exist for a binding, the binding will NOT be included.
55 *
56 * ```ts
57 * ctx.findByTag({controller: ANY_TAG_VALUE})
58 * ```
59 */
60const ANY_TAG_VALUE = (tagValue, tagName, tagMap) => tagName in tagMap;
61exports.ANY_TAG_VALUE = ANY_TAG_VALUE;
62/**
63 * Create a tag value matcher function that returns `true` if the target tag
64 * value equals to the item value or is an array that includes the item value.
65 * @param itemValues - A list of tag item value
66 */
67function includesTagValue(...itemValues) {
68 return tagValue => {
69 return itemValues.some(itemValue =>
70 // The tag value equals the item value
71 tagValue === itemValue ||
72 // The tag value contains the item value
73 (Array.isArray(tagValue) && tagValue.includes(itemValue)));
74 };
75}
76exports.includesTagValue = includesTagValue;
77/**
78 * Create a binding filter for the tag pattern
79 * @param tagPattern - Binding tag name, regexp, or object
80 */
81function filterByTag(tagPattern) {
82 let filter;
83 let regex = undefined;
84 if (tagPattern instanceof RegExp) {
85 // RegExp for tag names
86 regex = tagPattern;
87 }
88 if (typeof tagPattern === 'string' &&
89 (tagPattern.includes('*') || tagPattern.includes('?'))) {
90 // Wildcard tag name
91 regex = wildcardToRegExp(tagPattern);
92 }
93 if (regex != null) {
94 // RegExp or wildcard match
95 filter = b => b.tagNames.some(t => regex.test(t));
96 }
97 else if (typeof tagPattern === 'string') {
98 // Plain tag string match
99 filter = b => b.tagNames.includes(tagPattern);
100 }
101 else {
102 // Match tag name/value pairs
103 const tagMap = tagPattern;
104 filter = b => {
105 for (const t in tagMap) {
106 if (!matchTagValue(tagMap[t], t, b.tagMap))
107 return false;
108 }
109 // All tag name/value pairs match
110 return true;
111 };
112 }
113 // Set up binding tag for the filter
114 const tagFilter = filter;
115 tagFilter.bindingTagPattern = regex !== null && regex !== void 0 ? regex : tagPattern;
116 return tagFilter;
117}
118exports.filterByTag = filterByTag;
119function matchTagValue(tagValueOrMatcher, tagName, tagMap) {
120 const tagValue = tagMap[tagName];
121 if (tagValue === tagValueOrMatcher)
122 return true;
123 if (typeof tagValueOrMatcher === 'function') {
124 return tagValueOrMatcher(tagValue, tagName, tagMap);
125 }
126 return false;
127}
128/**
129 * Create a binding filter from key pattern
130 * @param keyPattern - Binding key/wildcard, regexp, or a filter function
131 */
132function filterByKey(keyPattern) {
133 if (typeof keyPattern === 'string') {
134 const regex = wildcardToRegExp(keyPattern);
135 return binding => regex.test(binding.key);
136 }
137 else if (keyPattern instanceof RegExp) {
138 return binding => keyPattern.test(binding.key);
139 }
140 else if (typeof keyPattern === 'function') {
141 return keyPattern;
142 }
143 return () => true;
144}
145exports.filterByKey = filterByKey;
146/**
147 * Convert a wildcard pattern to RegExp
148 * @param pattern - A wildcard string with `*` and `?` as special characters.
149 * - `*` matches zero or more characters except `.` and `:`
150 * - `?` matches exactly one character except `.` and `:`
151 */
152function wildcardToRegExp(pattern) {
153 // Escape reserved chars for RegExp:
154 // `- \ ^ $ + . ( ) | { } [ ] :`
155 let regexp = pattern.replace(/[\-\[\]\/\{\}\(\)\+\.\\\^\$\|\:]/g, '\\$&');
156 // Replace wildcard chars `*` and `?`
157 // `*` matches zero or more characters except `.` and `:`
158 // `?` matches one character except `.` and `:`
159 regexp = regexp.replace(/\*/g, '[^.:]*').replace(/\?/g, '[^.:]');
160 return new RegExp(`^${regexp}$`);
161}
162//# sourceMappingURL=binding-filter.js.map
\No newline at end of file