@omgbase/oqx 0.9.0 → 0.10.1
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/CHANGELOG.md +214 -0
- package/LICENSE +21 -0
- package/README.md +214 -8
- package/dist/engine.d.ts +1 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +32 -13
- package/dist/engine.js.map +1 -1
- package/dist/parser.js +6 -2
- package/dist/parser.js.map +1 -1
- package/dist/semantics.d.ts +15 -0
- package/dist/semantics.d.ts.map +1 -1
- package/dist/semantics.js +34 -0
- package/dist/semantics.js.map +1 -1
- package/package.json +27 -6
- package/src/engine.ts +34 -15
- package/src/parser.ts +5 -2
- package/src/semantics.ts +42 -0
package/src/semantics.ts
CHANGED
|
@@ -167,9 +167,51 @@ export function parseRangeString(s: string): OqxRange | null {
|
|
|
167
167
|
return null;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// ---- entries: the explicit record → collection bridge -----------------------
|
|
171
|
+
//
|
|
172
|
+
// A plain object is NOT iterable in OQX (`from ${obj}` is one row). `entries(x)`
|
|
173
|
+
// converts it explicitly into a collection of ENTRY values, each `{ key, value }`
|
|
174
|
+
// tagged (non-enumerably, so JSON/deepEqual see a plain record) so the engine
|
|
175
|
+
// can recognize one: when an entry becomes a query scope, the scope's ROW is
|
|
176
|
+
// the property's value (`$value`, bare names) and `$key` is scope metadata.
|
|
177
|
+
// Arrays yield numeric index keys; a Map yields its entries; null/scalars yield
|
|
178
|
+
// nothing. (Arrays never expose an implicit `$key` — `entries(arr)` is how you
|
|
179
|
+
// ask for the index.)
|
|
180
|
+
|
|
181
|
+
const ENTRY: unique symbol = Symbol.for("oqx.entry");
|
|
182
|
+
|
|
183
|
+
export interface OqxEntry {
|
|
184
|
+
readonly key: unknown;
|
|
185
|
+
readonly value: unknown;
|
|
186
|
+
readonly [ENTRY]: true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Construct an entry value (see entriesOf). */
|
|
190
|
+
export function makeEntry(key: unknown, value: unknown): OqxEntry {
|
|
191
|
+
const e = { key, value } as { key: unknown; value: unknown; [ENTRY]?: true };
|
|
192
|
+
Object.defineProperty(e, ENTRY, { value: true, enumerable: false });
|
|
193
|
+
return e as OqxEntry;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Whether a value is an entry produced by `entries()` / `makeEntry`. */
|
|
197
|
+
export function isEntry(v: unknown): v is OqxEntry {
|
|
198
|
+
return typeof v === "object" && v !== null && (v as { [ENTRY]?: unknown })[ENTRY] === true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The entries of a host value: object → own enumerable (key, value) pairs in
|
|
202
|
+
* insertion order; array → (index, element); Map → its entries; anything else
|
|
203
|
+
* (absent, scalars, ranges) → none. */
|
|
204
|
+
export function entriesOf(v: unknown): OqxEntry[] {
|
|
205
|
+
if (v == null || typeof v !== "object" || isRange(v)) return [];
|
|
206
|
+
if (Array.isArray(v)) return v.map((x, i) => makeEntry(i, x));
|
|
207
|
+
if (v instanceof Map) return Array.from(v, ([k, x]) => makeEntry(k, x));
|
|
208
|
+
return Object.entries(v as Record<string, unknown>).map(([k, x]) => makeEntry(k, x));
|
|
209
|
+
}
|
|
210
|
+
|
|
170
211
|
/** Free functions callable as `name(args)`. */
|
|
171
212
|
export const BUILTIN_FUNCTIONS: Record<string, (args: unknown[]) => unknown> = {
|
|
172
213
|
list: (args) => toList(args[0]),
|
|
214
|
+
entries: (args) => entriesOf(args[0]),
|
|
173
215
|
size: (args) => sizeOf(args[0]),
|
|
174
216
|
has: (args) => args[0] != null,
|
|
175
217
|
// Coerce a string to a range value (or pass a range through); anything else,
|