|
1 | | -// import type { ProcessedPub } from "contracts" |
2 | | - |
3 | | -// export const createPubProxy = (pub: ProcessedPub, communitySlug: string): any => { |
4 | | -// const valuesMap = new Map<string, ProcessedPub["values"][number]>() |
5 | | - |
6 | | -// // these are just so that if you do `$.values`/`$.out`/`$.fields` you can see what fields are available |
7 | | -// const fields: Record<string, undefined> = {} |
8 | | -// const relations: Record<string, undefined> = {} |
9 | | - |
10 | | -// for (const value of pub.values) { |
11 | | -// fields[value.fieldSlug] = undefined |
12 | | -// fields[value.fieldSlug.replace(`${communitySlug}:`, "")] = undefined |
13 | | -// if (value.relatedPub) { |
14 | | -// relations[value.fieldSlug] = undefined |
15 | | -// relations[value.fieldSlug.replace(`${communitySlug}:`, "")] = undefined |
16 | | -// } |
17 | | -// valuesMap.set(value.fieldSlug, value) |
18 | | -// } |
19 | | - |
20 | | -// const pubWithAdditionalFields = { ...pub, fields: undefined, out: relations } |
21 | | - |
22 | | -// return new Proxy(pubWithAdditionalFields, { |
23 | | -// get(target, prop) { |
24 | | -// const propStr = String(prop) |
25 | | -// const _lowerProp = propStr.toLowerCase() |
26 | | - |
27 | | -// if (prop === "fields") { |
28 | | -// return new Proxy(fields, { |
29 | | -// get(_, fieldSlug: string) { |
30 | | -// return fields[fieldSlug] || fields[fieldSlug.toLowerCase()] |
31 | | -// }, |
32 | | -// }) |
33 | | -// } |
34 | | -// if (prop === "values") { |
35 | | -// return new Proxy(fields, { |
36 | | -// get(_, fieldSlug: string) { |
37 | | -// const lowerFieldSlug = fieldSlug.toLowerCase() |
38 | | -// const val = |
39 | | -// valuesMap.get(`${communitySlug}:${fieldSlug}`) ?? |
40 | | -// valuesMap.get(fieldSlug) ?? |
41 | | -// valuesMap.get(`${communitySlug}:${lowerFieldSlug}`) ?? |
42 | | -// valuesMap.get(lowerFieldSlug) |
43 | | -// return val?.value |
44 | | -// }, |
45 | | -// }) |
46 | | -// } |
47 | | - |
48 | | -// if (prop === "out") { |
49 | | -// return new Proxy(relations, { |
50 | | -// get(_, fieldSlug: string) { |
51 | | -// if (typeof fieldSlug !== "string") { |
52 | | -// return undefined |
53 | | -// } |
54 | | - |
55 | | -// if (fieldSlug === "out") { |
56 | | -// return relations |
57 | | -// } |
58 | | - |
59 | | -// const lowerFieldSlug = fieldSlug.toLowerCase() |
60 | | - |
61 | | -// const val = |
62 | | -// valuesMap.get(`${communitySlug}:${fieldSlug}`) ?? |
63 | | -// valuesMap.get(fieldSlug) ?? |
64 | | -// valuesMap.get(`${communitySlug}:${lowerFieldSlug}`) ?? |
65 | | -// valuesMap.get(lowerFieldSlug) |
66 | | -// if (val && "relatedPub" in val && val.relatedPub) { |
67 | | -// return createPubProxy(val.relatedPub, communitySlug) |
68 | | -// } |
69 | | -// return undefined |
70 | | -// }, |
71 | | -// }) |
72 | | -// } |
73 | | - |
74 | | -// if (prop === "in") { |
75 | | -// return new Proxy(relations, { |
76 | | -// get(_, fieldSlug: string) { |
77 | | -// const _lowerFieldSlug = fieldSlug.toLowerCase() |
78 | | -// // For "in", we look for pubs that point to this one via fieldSlug |
79 | | -// // This proxy doesn't currently support "in" metadata easily as it's not pre-loaded in valuesMap in the same way. |
80 | | -// // However, if we had it, we'd look it up here. |
81 | | -// return undefined |
82 | | -// }, |
83 | | -// }) |
84 | | -// } |
85 | | - |
86 | | -// return target[prop as keyof typeof target] |
87 | | -// }, |
88 | | -// }) |
89 | | -// } |
90 | | - |
91 | | -import type { ProcessedPub } from "contracts" |
92 | | - |
93 | | -// properties that should never be forwarded through the proxy |
94 | | -const BLOCKED_PROPS = new Set([ |
95 | | - "then", |
96 | | - "catch", |
97 | | - "finally", |
98 | | - "constructor", |
99 | | - "__proto__", |
100 | | - "prototype", |
101 | | - "$$typeof", |
102 | | - "toJSON", |
103 | | - "valueOf", |
104 | | - "toString", |
105 | | - "hasOwnProperty", |
106 | | - "isPrototypeOf", |
107 | | - "propertyIsEnumerable", |
108 | | -]) |
109 | | - |
110 | | -const isBlockedProp = (prop: string | symbol): boolean => { |
111 | | - if (typeof prop === "symbol") return true |
112 | | - return BLOCKED_PROPS.has(prop) |
113 | | -} |
114 | | - |
115 | | -// creates a proxy that allows case-insensitive lookup but also returns the full object when iterated |
116 | | -const createLookupProxy = <T>( |
117 | | - data: Record<string, T>, |
118 | | - communitySlug: string |
119 | | -): Record<string, T> => { |
120 | | - return new Proxy(data, { |
121 | | - get(target, prop) { |
122 | | - if (isBlockedProp(prop)) return undefined |
123 | | - const propStr = String(prop) |
124 | | - // direct match |
125 | | - if (propStr in target) return target[propStr] |
126 | | - // try with community prefix |
127 | | - const prefixed = `${communitySlug}:${propStr}` |
128 | | - if (prefixed in target) return target[prefixed] |
129 | | - // try lowercase |
130 | | - const lower = propStr.toLowerCase() |
131 | | - if (lower in target) return target[lower] |
132 | | - const prefixedLower = `${communitySlug}:${lower}` |
133 | | - if (prefixedLower in target) return target[prefixedLower] |
134 | | - return undefined |
135 | | - }, |
136 | | - has(target, prop) { |
137 | | - if (isBlockedProp(prop)) return false |
138 | | - return prop in target |
139 | | - }, |
140 | | - ownKeys(target) { |
141 | | - return Reflect.ownKeys(target) |
142 | | - }, |
143 | | - getOwnPropertyDescriptor(target, prop) { |
144 | | - return Object.getOwnPropertyDescriptor(target, prop) |
145 | | - }, |
146 | | - }) |
147 | | -} |
148 | | - |
149 | | -export const createPubProxy = ( |
150 | | - pub: ProcessedPub, |
151 | | - communitySlug: string |
152 | | -): Record<string, unknown> => { |
153 | | - // build plain objects for all lookups |
154 | | - const fields: Record<string, true> = {} |
155 | | - const values: Record<string, unknown> = {} |
156 | | - const out: Record<string, Record<string, unknown>> = {} |
157 | | - |
158 | | - for (const v of pub.values) { |
159 | | - const shortSlug = v.fieldSlug.replace(`${communitySlug}:`, "") |
160 | | - // use short slug as primary key to avoid duplicates |
161 | | - fields[shortSlug] = true |
162 | | - values[shortSlug] = v.value |
163 | | - |
164 | | - if (v.relatedPub) { |
165 | | - out[shortSlug] = createPubProxy(v.relatedPub, communitySlug) |
166 | | - } |
167 | | - } |
168 | | - |
169 | | - const fieldsProxy = createLookupProxy(fields, communitySlug) |
170 | | - const valuesProxy = createLookupProxy(values, communitySlug) |
171 | | - const outProxy = createLookupProxy(out, communitySlug) |
172 | | - |
173 | | - // build the base object with all pub properties except values (which we override) |
174 | | - const base: Record<string, unknown> = {} |
175 | | - for (const key of Object.keys(pub)) { |
176 | | - if (key === "values") continue |
177 | | - base[key] = pub[key as keyof ProcessedPub] |
178 | | - } |
179 | | - |
180 | | - base.fields = fieldsProxy |
181 | | - base.values = valuesProxy |
182 | | - base.out = outProxy |
183 | | - base.in = {} // not implemented yet |
184 | | - |
185 | | - return new Proxy(base, { |
186 | | - get(target, prop) { |
187 | | - if (isBlockedProp(prop)) return undefined |
188 | | - return target[prop as string] |
189 | | - }, |
190 | | - has(target, prop) { |
191 | | - if (isBlockedProp(prop)) return false |
192 | | - return prop in target |
193 | | - }, |
194 | | - ownKeys(target) { |
195 | | - return Reflect.ownKeys(target) |
196 | | - }, |
197 | | - getOwnPropertyDescriptor(target, prop) { |
198 | | - return Object.getOwnPropertyDescriptor(target, prop) |
199 | | - }, |
200 | | - }) |
201 | | -} |
| 1 | +export { createPubProxy, type IncomingRelations } from "contracts" |
0 commit comments