@blackwell-systems/gcf 0.1.0 → 0.1.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.
- package/README.md +26 -1
- package/dist/generic.d.ts +8 -0
- package/dist/generic.d.ts.map +1 -0
- package/dist/generic.js +137 -0
- package/dist/generic.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/generic.ts +154 -0
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -119,11 +119,36 @@ const output = encodeDelta(delta);
|
|
|
119
119
|
|
|
120
120
|
81.2% savings on re-queries where the pack changed slightly.
|
|
121
121
|
|
|
122
|
+
## Generic Encoding
|
|
123
|
+
|
|
124
|
+
Encode any JS value (not just graph payloads) into GCF tabular format:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { encodeGeneric } from '@blackwell-systems/gcf';
|
|
128
|
+
|
|
129
|
+
const output = encodeGeneric({
|
|
130
|
+
employees: [
|
|
131
|
+
{ id: 1, name: 'Alice', department: 'Engineering', salary: 95000 },
|
|
132
|
+
{ id: 2, name: 'Bob', department: 'Sales', salary: 72000 },
|
|
133
|
+
],
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Output:
|
|
138
|
+
```
|
|
139
|
+
## employees [2]{id,name,department,salary}
|
|
140
|
+
1|Alice|Engineering|95000
|
|
141
|
+
2|Bob|Sales|72000
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Works on objects, arrays, and primitives. Arrays of uniform objects get tabular rows. Nested objects use `## key` section headers.
|
|
145
|
+
|
|
122
146
|
## API
|
|
123
147
|
|
|
124
148
|
| Function | Description |
|
|
125
149
|
|----------|-------------|
|
|
126
|
-
| `encode(p: Payload): string` | Encode a payload to GCF text |
|
|
150
|
+
| `encode(p: Payload): string` | Encode a graph payload to GCF text |
|
|
151
|
+
| `encodeGeneric(data: unknown): string` | Encode any value to GCF tabular format |
|
|
127
152
|
| `decode(input: string): Payload` | Parse GCF text back to a Payload |
|
|
128
153
|
| `encodeWithSession(p: Payload, s: Session): string` | Encode with session deduplication |
|
|
129
154
|
| `encodeDelta(d: DeltaPayload): string` | Encode a delta (added/removed only) |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generic.d.ts","sourceRoot":"","sources":["../src/generic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqIH;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAenD"}
|
package/dist/generic.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic encoder: converts any JS value into GCF tabular format.
|
|
3
|
+
*/
|
|
4
|
+
function formatValue(value) {
|
|
5
|
+
if (value === null || value === undefined)
|
|
6
|
+
return '-';
|
|
7
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
8
|
+
return String(value);
|
|
9
|
+
const s = String(value);
|
|
10
|
+
if (s.includes('|') || s.includes('\n') || s === '')
|
|
11
|
+
return JSON.stringify(s);
|
|
12
|
+
return s;
|
|
13
|
+
}
|
|
14
|
+
function indent(depth) {
|
|
15
|
+
return ' '.repeat(depth);
|
|
16
|
+
}
|
|
17
|
+
function isUniformObjectArray(arr) {
|
|
18
|
+
if (arr.length === 0)
|
|
19
|
+
return false;
|
|
20
|
+
const objects = arr.filter((item) => typeof item === 'object' && item !== null && !Array.isArray(item));
|
|
21
|
+
if (objects.length !== arr.length)
|
|
22
|
+
return false;
|
|
23
|
+
const referenceKeys = Object.keys(objects[0]).sort().join(',');
|
|
24
|
+
const sampleSize = Math.min(5, objects.length);
|
|
25
|
+
const refSet = new Set(Object.keys(objects[0]));
|
|
26
|
+
for (let i = 1; i < sampleSize; i++) {
|
|
27
|
+
const keys = Object.keys(objects[i]);
|
|
28
|
+
const currentSet = new Set(keys);
|
|
29
|
+
// 70% overlap check.
|
|
30
|
+
const union = new Set([...refSet, ...currentSet]);
|
|
31
|
+
const intersection = [...refSet].filter((k) => currentSet.has(k));
|
|
32
|
+
if (intersection.length / union.size < 0.7)
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function encodeArray(arr, name, lines, depth) {
|
|
38
|
+
const prefix = indent(depth);
|
|
39
|
+
if (isUniformObjectArray(arr)) {
|
|
40
|
+
const objects = arr;
|
|
41
|
+
// Collect all keys across items.
|
|
42
|
+
const allKeys = new Set();
|
|
43
|
+
for (const obj of objects) {
|
|
44
|
+
for (const k of Object.keys(obj))
|
|
45
|
+
allKeys.add(k);
|
|
46
|
+
}
|
|
47
|
+
// Separate primitive fields from nested fields.
|
|
48
|
+
const primitiveFields = [];
|
|
49
|
+
const nestedFields = [];
|
|
50
|
+
for (const key of allKeys) {
|
|
51
|
+
const sample = objects.find((o) => o[key] !== undefined)?.[key];
|
|
52
|
+
if (typeof sample === 'object' &&
|
|
53
|
+
sample !== null) {
|
|
54
|
+
nestedFields.push(key);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
primitiveFields.push(key);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
lines.push(`${prefix}## ${name} [${arr.length}]{${primitiveFields.join(',')}}`);
|
|
61
|
+
if (nestedFields.length === 0) {
|
|
62
|
+
// Pure flat rows: no @id prefix.
|
|
63
|
+
for (const obj of objects) {
|
|
64
|
+
const vals = primitiveFields.map((f) => formatValue(obj[f]));
|
|
65
|
+
lines.push(`${prefix}${vals.join('|')}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Rows with nested fields: @N prefix.
|
|
70
|
+
for (let i = 0; i < objects.length; i++) {
|
|
71
|
+
const obj = objects[i];
|
|
72
|
+
const vals = primitiveFields.map((f) => formatValue(obj[f]));
|
|
73
|
+
lines.push(`${prefix}@${i} ${vals.join('|')}`);
|
|
74
|
+
for (const nk of nestedFields) {
|
|
75
|
+
const nv = obj[nk];
|
|
76
|
+
if (nv === undefined || nv === null)
|
|
77
|
+
continue;
|
|
78
|
+
if (Array.isArray(nv)) {
|
|
79
|
+
encodeArray(nv, nk, lines, depth + 1);
|
|
80
|
+
}
|
|
81
|
+
else if (typeof nv === 'object') {
|
|
82
|
+
encodeObject(nv, nk, lines, depth + 1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// Non-uniform array.
|
|
90
|
+
lines.push(`${prefix}## ${name} [${arr.length}]`);
|
|
91
|
+
for (let i = 0; i < arr.length; i++) {
|
|
92
|
+
const item = arr[i];
|
|
93
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
94
|
+
lines.push(`${prefix}@${i}`);
|
|
95
|
+
encodeObject(item, null, lines, depth + 1);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
lines.push(`${prefix}@${i} ${formatValue(item)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function encodeObject(obj, name, lines, depth) {
|
|
104
|
+
const prefix = indent(depth);
|
|
105
|
+
if (name !== null) {
|
|
106
|
+
lines.push(`${prefix}## ${name}`);
|
|
107
|
+
}
|
|
108
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
encodeArray(value, key, lines, depth);
|
|
111
|
+
}
|
|
112
|
+
else if (typeof value === 'object' && value !== null) {
|
|
113
|
+
encodeObject(value, key, lines, depth + 1);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
lines.push(`${prefix}${key}=${formatValue(value)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Encode any JS value into GCF tabular format.
|
|
122
|
+
*/
|
|
123
|
+
export function encodeGeneric(data) {
|
|
124
|
+
// Primitives.
|
|
125
|
+
if (data === null || data === undefined || typeof data !== 'object') {
|
|
126
|
+
return String(data);
|
|
127
|
+
}
|
|
128
|
+
const lines = [];
|
|
129
|
+
if (Array.isArray(data)) {
|
|
130
|
+
encodeArray(data, 'root', lines, 0);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
encodeObject(data, null, lines, 0);
|
|
134
|
+
}
|
|
135
|
+
return lines.join('\n') + '\n';
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=generic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generic.js","sourceRoot":"","sources":["../src/generic.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
package/package.json
CHANGED
package/src/generic.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic encoder: converts any JS value into GCF tabular format.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
function formatValue(value: unknown): string {
|
|
6
|
+
if (value === null || value === undefined) return '-';
|
|
7
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
8
|
+
const s = String(value);
|
|
9
|
+
if (s.includes('|') || s.includes('\n') || s === '') return JSON.stringify(s);
|
|
10
|
+
return s;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function indent(depth: number): string {
|
|
14
|
+
return ' '.repeat(depth);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isUniformObjectArray(arr: unknown[]): boolean {
|
|
18
|
+
if (arr.length === 0) return false;
|
|
19
|
+
const objects = arr.filter(
|
|
20
|
+
(item): item is Record<string, unknown> =>
|
|
21
|
+
typeof item === 'object' && item !== null && !Array.isArray(item),
|
|
22
|
+
);
|
|
23
|
+
if (objects.length !== arr.length) return false;
|
|
24
|
+
|
|
25
|
+
const referenceKeys = Object.keys(objects[0]).sort().join(',');
|
|
26
|
+
const sampleSize = Math.min(5, objects.length);
|
|
27
|
+
const refSet = new Set(Object.keys(objects[0]));
|
|
28
|
+
|
|
29
|
+
for (let i = 1; i < sampleSize; i++) {
|
|
30
|
+
const keys = Object.keys(objects[i]);
|
|
31
|
+
const currentSet = new Set(keys);
|
|
32
|
+
// 70% overlap check.
|
|
33
|
+
const union = new Set([...refSet, ...currentSet]);
|
|
34
|
+
const intersection = [...refSet].filter((k) => currentSet.has(k));
|
|
35
|
+
if (intersection.length / union.size < 0.7) return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function encodeArray(
|
|
42
|
+
arr: unknown[],
|
|
43
|
+
name: string,
|
|
44
|
+
lines: string[],
|
|
45
|
+
depth: number,
|
|
46
|
+
): void {
|
|
47
|
+
const prefix = indent(depth);
|
|
48
|
+
|
|
49
|
+
if (isUniformObjectArray(arr)) {
|
|
50
|
+
const objects = arr as Record<string, unknown>[];
|
|
51
|
+
|
|
52
|
+
// Collect all keys across items.
|
|
53
|
+
const allKeys = new Set<string>();
|
|
54
|
+
for (const obj of objects) {
|
|
55
|
+
for (const k of Object.keys(obj)) allKeys.add(k);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Separate primitive fields from nested fields.
|
|
59
|
+
const primitiveFields: string[] = [];
|
|
60
|
+
const nestedFields: string[] = [];
|
|
61
|
+
for (const key of allKeys) {
|
|
62
|
+
const sample = objects.find((o) => o[key] !== undefined)?.[key];
|
|
63
|
+
if (
|
|
64
|
+
typeof sample === 'object' &&
|
|
65
|
+
sample !== null
|
|
66
|
+
) {
|
|
67
|
+
nestedFields.push(key);
|
|
68
|
+
} else {
|
|
69
|
+
primitiveFields.push(key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
lines.push(`${prefix}## ${name} [${arr.length}]{${primitiveFields.join(',')}}`);
|
|
74
|
+
|
|
75
|
+
if (nestedFields.length === 0) {
|
|
76
|
+
// Pure flat rows: no @id prefix.
|
|
77
|
+
for (const obj of objects) {
|
|
78
|
+
const vals = primitiveFields.map((f) => formatValue(obj[f]));
|
|
79
|
+
lines.push(`${prefix}${vals.join('|')}`);
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// Rows with nested fields: @N prefix.
|
|
83
|
+
for (let i = 0; i < objects.length; i++) {
|
|
84
|
+
const obj = objects[i];
|
|
85
|
+
const vals = primitiveFields.map((f) => formatValue(obj[f]));
|
|
86
|
+
lines.push(`${prefix}@${i} ${vals.join('|')}`);
|
|
87
|
+
for (const nk of nestedFields) {
|
|
88
|
+
const nv = obj[nk];
|
|
89
|
+
if (nv === undefined || nv === null) continue;
|
|
90
|
+
if (Array.isArray(nv)) {
|
|
91
|
+
encodeArray(nv, nk, lines, depth + 1);
|
|
92
|
+
} else if (typeof nv === 'object') {
|
|
93
|
+
encodeObject(nv as Record<string, unknown>, nk, lines, depth + 1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// Non-uniform array.
|
|
100
|
+
lines.push(`${prefix}## ${name} [${arr.length}]`);
|
|
101
|
+
for (let i = 0; i < arr.length; i++) {
|
|
102
|
+
const item = arr[i];
|
|
103
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
104
|
+
lines.push(`${prefix}@${i}`);
|
|
105
|
+
encodeObject(item as Record<string, unknown>, null, lines, depth + 1);
|
|
106
|
+
} else {
|
|
107
|
+
lines.push(`${prefix}@${i} ${formatValue(item)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function encodeObject(
|
|
114
|
+
obj: Record<string, unknown>,
|
|
115
|
+
name: string | null,
|
|
116
|
+
lines: string[],
|
|
117
|
+
depth: number,
|
|
118
|
+
): void {
|
|
119
|
+
const prefix = indent(depth);
|
|
120
|
+
|
|
121
|
+
if (name !== null) {
|
|
122
|
+
lines.push(`${prefix}## ${name}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
encodeArray(value, key, lines, depth);
|
|
128
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
129
|
+
encodeObject(value as Record<string, unknown>, key, lines, depth + 1);
|
|
130
|
+
} else {
|
|
131
|
+
lines.push(`${prefix}${key}=${formatValue(value)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Encode any JS value into GCF tabular format.
|
|
138
|
+
*/
|
|
139
|
+
export function encodeGeneric(data: unknown): string {
|
|
140
|
+
// Primitives.
|
|
141
|
+
if (data === null || data === undefined || typeof data !== 'object') {
|
|
142
|
+
return String(data);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const lines: string[] = [];
|
|
146
|
+
|
|
147
|
+
if (Array.isArray(data)) {
|
|
148
|
+
encodeArray(data, 'root', lines, 0);
|
|
149
|
+
} else {
|
|
150
|
+
encodeObject(data as Record<string, unknown>, null, lines, 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return lines.join('\n') + '\n';
|
|
154
|
+
}
|
package/src/index.ts
CHANGED