js-bao 0.12.0-alpha.3 → 0.12.0-alpha.5
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 +91 -20
- package/dist/browser.cjs +12451 -15535
- package/dist/browser.d.cts +4541 -6336
- package/dist/browser.d.ts +4541 -6336
- package/dist/browser.js +12449 -15532
- package/dist/client.cjs +203 -518
- package/dist/client.d.cts +1971 -3172
- package/dist/client.d.ts +1971 -3172
- package/dist/client.js +202 -514
- package/dist/cloudflare-do.cjs +3559 -4190
- package/dist/cloudflare-do.d.cts +2830 -4257
- package/dist/cloudflare-do.d.ts +2830 -4257
- package/dist/cloudflare-do.js +3558 -4187
- package/dist/cloudflare.cjs +229 -544
- package/dist/cloudflare.d.cts +1975 -3176
- package/dist/cloudflare.d.ts +1975 -3176
- package/dist/cloudflare.js +228 -540
- package/dist/codegen-v2.cjs +132 -429
- package/dist/codegen.cjs +10 -40
- package/dist/format2-worker.js +5107 -3545
- package/dist/index.cjs +12980 -14709
- package/dist/index.d.cts +4274 -5385
- package/dist/index.d.ts +4274 -5385
- package/dist/index.js +12915 -14653
- package/dist/node.cjs +12883 -15918
- package/dist/node.d.cts +5439 -7375
- package/dist/node.d.ts +5439 -7375
- package/dist/node.js +12833 -15841
- package/dist/record-log.cjs +1413 -0
- package/dist/record-log.d.cts +955 -0
- package/dist/record-log.d.ts +955 -0
- package/dist/record-log.js +1390 -0
- package/dist/sqlite3.wasm +0 -0
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1023,6 +1023,12 @@ const electronicsCount = await Product.count({
|
|
|
1023
1023
|
|
|
1024
1024
|
Perform complex data analysis with grouping, statistical operations, and faceting.
|
|
1025
1025
|
|
|
1026
|
+
`aggregate` answers one row per group: the `groupBy` values under `group`, as
|
|
1027
|
+
stored (`null` for a missing field), and each operation's value beside it under
|
|
1028
|
+
`count` or `<type>_<field>` — even when it is the only operation. Rows come back
|
|
1029
|
+
in `sort` order; no matching records gives `[]`. Two `groupBy` entries that
|
|
1030
|
+
would share a key in `group` throw `Ambiguous aggregation group keys`.
|
|
1031
|
+
|
|
1026
1032
|
### Basic Aggregation
|
|
1027
1033
|
|
|
1028
1034
|
```typescript
|
|
@@ -1031,7 +1037,11 @@ const categoryCounts = await Product.aggregate({
|
|
|
1031
1037
|
groupBy: ["category"],
|
|
1032
1038
|
operations: [{ type: "count" }],
|
|
1033
1039
|
});
|
|
1034
|
-
// Result:
|
|
1040
|
+
// Result: [
|
|
1041
|
+
// { group: { category: "Electronics" }, count: 25 },
|
|
1042
|
+
// { group: { category: "Books" }, count: 18 },
|
|
1043
|
+
// { group: { category: "Clothing" }, count: 12 }
|
|
1044
|
+
// ]
|
|
1035
1045
|
|
|
1036
1046
|
// Multiple statistical operations
|
|
1037
1047
|
const categoryStats = await Product.aggregate({
|
|
@@ -1044,8 +1054,9 @@ const categoryStats = await Product.aggregate({
|
|
|
1044
1054
|
{ type: "max", field: "price" },
|
|
1045
1055
|
],
|
|
1046
1056
|
});
|
|
1047
|
-
// Result:
|
|
1048
|
-
//
|
|
1057
|
+
// Result: [
|
|
1058
|
+
// {
|
|
1059
|
+
// group: { category: "Electronics" },
|
|
1049
1060
|
// count: 25,
|
|
1050
1061
|
// avg_price: 299.99,
|
|
1051
1062
|
// sum_price: 7499.75,
|
|
@@ -1053,7 +1064,7 @@ const categoryStats = await Product.aggregate({
|
|
|
1053
1064
|
// max_price: 1299.99
|
|
1054
1065
|
// },
|
|
1055
1066
|
// ...
|
|
1056
|
-
//
|
|
1067
|
+
// ]
|
|
1057
1068
|
```
|
|
1058
1069
|
|
|
1059
1070
|
### Multi-Dimensional Grouping
|
|
@@ -1064,15 +1075,11 @@ const salesData = await Product.aggregate({
|
|
|
1064
1075
|
groupBy: ["category", "brand"],
|
|
1065
1076
|
operations: [{ type: "count" }, { type: "sum", field: "revenue" }],
|
|
1066
1077
|
});
|
|
1067
|
-
// Result:
|
|
1068
|
-
// "Electronics":
|
|
1069
|
-
//
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1072
|
-
// "Books": {
|
|
1073
|
-
// "Penguin": { count: 25, sum_revenue: 450 }
|
|
1074
|
-
// }
|
|
1075
|
-
// }
|
|
1078
|
+
// Result: [
|
|
1079
|
+
// { group: { category: "Electronics", brand: "Apple" }, count: 12, sum_revenue: 15000 },
|
|
1080
|
+
// { group: { category: "Electronics", brand: "Samsung" }, count: 8, sum_revenue: 9500 },
|
|
1081
|
+
// { group: { category: "Books", brand: "Penguin" }, count: 25, sum_revenue: 450 }
|
|
1082
|
+
// ]
|
|
1076
1083
|
```
|
|
1077
1084
|
|
|
1078
1085
|
### StringSet Aggregation
|
|
@@ -1105,14 +1112,21 @@ const tagCounts = await Article.aggregate({
|
|
|
1105
1112
|
groupBy: ["tags"], // StringSet faceting
|
|
1106
1113
|
operations: [{ type: "count" }],
|
|
1107
1114
|
});
|
|
1108
|
-
// Result:
|
|
1115
|
+
// Result: [
|
|
1116
|
+
// { group: { tags: "javascript" }, count: 45 },
|
|
1117
|
+
// { group: { tags: "react" }, count: 32 },
|
|
1118
|
+
// { group: { tags: "tutorial" }, count: 28 }
|
|
1119
|
+
// ]
|
|
1109
1120
|
|
|
1110
1121
|
// Membership-based grouping (articles that have specific tag vs don't)
|
|
1111
1122
|
const urgentCounts = await Article.aggregate({
|
|
1112
1123
|
groupBy: [{ field: "tags", contains: "urgent" }],
|
|
1113
1124
|
operations: [{ type: "count" }],
|
|
1114
1125
|
});
|
|
1115
|
-
// Result:
|
|
1126
|
+
// Result: [
|
|
1127
|
+
// { group: { has_tags_urgent: "true" }, count: 5 },
|
|
1128
|
+
// { group: { has_tags_urgent: "false" }, count: 120 }
|
|
1129
|
+
// ]
|
|
1116
1130
|
|
|
1117
1131
|
// Complex aggregation with filtering and sorting
|
|
1118
1132
|
const topTags = await Article.aggregate({
|
|
@@ -1128,7 +1142,11 @@ const topTags = await Article.aggregate({
|
|
|
1128
1142
|
|
|
1129
1143
|
```typescript
|
|
1130
1144
|
interface AggregationOptions {
|
|
1131
|
-
groupBy: (
|
|
1145
|
+
groupBy: (
|
|
1146
|
+
| string // a plain field, or a StringSet facet
|
|
1147
|
+
| { field: string; contains: string } // StringSet membership: has_<field>_<contains>
|
|
1148
|
+
| { field: string; period: "month" | "quarter" | "year" } // date period: <field>_<period>
|
|
1149
|
+
)[];
|
|
1132
1150
|
operations: {
|
|
1133
1151
|
type: "count" | "sum" | "avg" | "min" | "max";
|
|
1134
1152
|
field?: string; // Required for sum, avg, min, max
|
|
@@ -1143,6 +1161,22 @@ interface AggregationOptions {
|
|
|
1143
1161
|
}
|
|
1144
1162
|
```
|
|
1145
1163
|
|
|
1164
|
+
A period level groups a `date` (or `string`) field by calendar month, quarter
|
|
1165
|
+
or year. Its value in `group` is `YYYY-MM`, `YYYY-Qn` or `YYYY`, read from the
|
|
1166
|
+
stored text with no time-zone conversion (a UTC ISO timestamp buckets by its
|
|
1167
|
+
UTC date); a missing or non-date value is `null`. The keys sort
|
|
1168
|
+
chronologically, so `sort` can name them:
|
|
1169
|
+
|
|
1170
|
+
```typescript
|
|
1171
|
+
const monthly = await Transaction.aggregate({
|
|
1172
|
+
filter: { date: { $gte: "2025-11-01", $lte: "2026-10-31" } },
|
|
1173
|
+
groupBy: ["categoryId", { field: "date", period: "month" }],
|
|
1174
|
+
operations: [{ type: "sum", field: "amount" }, { type: "count" }],
|
|
1175
|
+
sort: { field: "date_month", direction: 1 },
|
|
1176
|
+
});
|
|
1177
|
+
// [{ group: { categoryId: "food", date_month: "2025-11" }, sum_amount: -50, count: 2 }, ...]
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1146
1180
|
## StringSet Fields
|
|
1147
1181
|
|
|
1148
1182
|
StringSet is a special field type optimized for tag-like data, providing efficient membership queries and faceting capabilities.
|
|
@@ -1254,7 +1288,10 @@ const tagStats = await Article.aggregate({
|
|
|
1254
1288
|
groupBy: [{ field: "tags", contains: "tutorial" }],
|
|
1255
1289
|
operations: [{ type: "count" }],
|
|
1256
1290
|
});
|
|
1257
|
-
// Result:
|
|
1291
|
+
// Result: [
|
|
1292
|
+
// { group: { has_tags_tutorial: "true" }, count: 25 },
|
|
1293
|
+
// { group: { has_tags_tutorial: "false" }, count: 75 }
|
|
1294
|
+
// ]
|
|
1258
1295
|
```
|
|
1259
1296
|
|
|
1260
1297
|
## Database Engine Specifics
|
|
@@ -1556,9 +1593,43 @@ See the `examples/` directory for complete working examples:
|
|
|
1556
1593
|
|
|
1557
1594
|
## Release notes
|
|
1558
1595
|
|
|
1559
|
-
Changes
|
|
1560
|
-
|
|
1561
|
-
|
|
1596
|
+
Changes a consumer of this package can observe, including the on-disk
|
|
1597
|
+
behaviour of `js-bao/cloudflare/do` (`DurableObjectEngine`). Newest first.
|
|
1598
|
+
|
|
1599
|
+
- **Breaking: format 2 (large document) is removed** (0.12.0). Every
|
|
1600
|
+
`models/format2*` export that served the epoch overlay is gone — the record
|
|
1601
|
+
store and its asynchronous host, the overlay observer and fold, the epoch
|
|
1602
|
+
handoff and rotation, the cold start and snapshot loader, the offline window
|
|
1603
|
+
(`DocumentOfflineWindowError`) and offline replay — with `utils/largeDocuments`
|
|
1604
|
+
and `utils/overlaySql`. `InitJsBaoOptions` loses `largeDocumentStorage` and
|
|
1605
|
+
`InitJsBaoResult` its `format2*` hooks and `whenFormat2ProjectionSettled`;
|
|
1606
|
+
`connectDocument` refuses `documentFormat: 2` with `DOCUMENT_FORMAT_UNSUPPORTED`.
|
|
1607
|
+
The OPFS worker and broker, `Format2StoreSql`, `createFormat2Sql()`,
|
|
1608
|
+
`largeDocumentStoreScope` and the row cache stay: a record-log document
|
|
1609
|
+
(`documentFormat: 3`) runs on them. The worker answers `serve`,
|
|
1610
|
+
`storedEpoch`, `exportLegacy`, `importLegacy` and `dropLegacy` as unknown
|
|
1611
|
+
commands.
|
|
1612
|
+
- **Breaking: `createModelClass` is removed** (#4285). It is no longer exported
|
|
1613
|
+
from `js-bao` (or re-exported from `js-bao-wss-client`). Define a model as
|
|
1614
|
+
`class X extends BaseModel {}` and attach its schema with
|
|
1615
|
+
`attachAndRegisterModel(X, schema)`, as [Defining Models](#defining-models-schema-first-no-decorators)
|
|
1616
|
+
shows; instance methods, getters and statics go on the class body. The v1
|
|
1617
|
+
codegen (`js-bao-codegen`) no longer recognises a `createModelClass(...)`
|
|
1618
|
+
call as a model definition; it reads the class-based pattern as before.
|
|
1619
|
+
- **New stores get a keyed StringSet table** (#3881). A store the engine
|
|
1620
|
+
creates declares `stringset_index` as `PRIMARY KEY (_type, _record_id, field,
|
|
1621
|
+
value) WITHOUT ROWID` with `idx_ss_type_field_value` as its one named index,
|
|
1622
|
+
so a StringSet value costs two rows written instead of three.
|
|
1623
|
+
`DURABLE_OBJECT_SCHEMA_OPTIONS.memberShape` is `"keyed"`, and
|
|
1624
|
+
`engine.memberTableShape` answers `"keyed"` for such a store. An existing
|
|
1625
|
+
store keeps its record-first or field-first table; all three shapes return
|
|
1626
|
+
the same results. On the platform, a plain document converts at its next
|
|
1627
|
+
projection rebuild; databases and large documents keep their shape.
|
|
1628
|
+
`rebuildMemberTable` now builds the keyed table. Rollback floor: a keyed table
|
|
1629
|
+
has no `rowid`, so once a keyed store exists do not roll back past the
|
|
1630
|
+
release that first returned StringSet values sorted by code point (it reads
|
|
1631
|
+
`ORDER BY value`); an older release fails on a keyed table with `no such
|
|
1632
|
+
column: rowid`.
|
|
1562
1633
|
- **New stores get a record-first StringSet table** (#3877). A store the
|
|
1563
1634
|
engine creates declares `stringset_index` with `UNIQUE(_type, _record_id,
|
|
1564
1635
|
field, value)` (record-first) instead of `UNIQUE(_type, field, _record_id,
|