Skip to content

Commit dbb7e19

Browse files
authored
Support params in queries and transactions (#950)
1 parent 29d8e38 commit dbb7e19

33 files changed

+936
-634
lines changed

client/package.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
]
1313
},
1414
"scripts": {
15-
"build": "turbo run build --cache-dir=.turbo",
16-
"dev": "turbo run dev --parallel",
17-
"test": "turbo run test:ci",
18-
"bench": "turbo run bench:ci",
15+
"build": "turbo run build --cache-dir=.turbo --no-update-notifier",
16+
"dev": "turbo run dev --parallel --no-update-notifier",
17+
"test": "turbo run test:ci --no-update-notifier",
18+
"bench": "turbo run bench:ci --no-update-notifier",
1919
"format": "prettier --write --ignore-path ../.gitignore --config ./.prettierrc \"**/*.{ts,tsx,js,jsx,json,md}\"",
2020
"check-format": "prettier --check --ignore-path ../.gitignore --config ./.prettierrc \"**/*.{ts,tsx,js,jsx,json,md}\"",
21-
"publish-packages": "turbo run publish-package --filter=\"./packages/*\"",
22-
"build-packages": "turbo run build --filter=\"./packages/*\" --cache-dir=.turbo",
23-
"build-sandbox": "turbo run build --filter=\"./sandbox/*\" --cache-dir=.turbo"
21+
"publish-packages": "turbo run publish-package --filter=\"./packages/*\" --no-update-notifier",
22+
"build-packages": "turbo run build --filter=\"./packages/*\" --cache-dir=.turbo --no-update-notifier",
23+
"build-sandbox": "turbo run build --filter=\"./sandbox/*\" --cache-dir=.turbo --no-update-notifier"
2424
},
2525
"devDependencies": {
2626
"prettier": "^3.3.3",

client/packages/admin/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"scripts": {
1010
"test": "vitest",
1111
"build": "rm -rf dist; npm run build:main && npm run build:module",
12-
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck",
13-
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck",
12+
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck --preserveWatchOutput",
13+
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck --preserveWatchOutput",
1414
"dev": "run-p dev:main dev:module",
1515
"build:main": "tsc -p tsconfig.json",
1616
"build:module": "tsc -p tsconfig.module.json",

client/packages/admin/src/index.ts

+19-14
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ function init<
224224
*/
225225
const init_experimental = init;
226226

227+
function steps(inputChunks) {
228+
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
229+
return chunks.flatMap(getOps);
230+
}
231+
227232
/**
228233
*
229234
* The first step: init your application!
@@ -335,12 +340,10 @@ class InstantAdmin<
335340
transact = (
336341
inputChunks: TransactionChunk<any, any> | TransactionChunk<any, any>[],
337342
) => {
338-
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
339-
const steps = chunks.flatMap((tx) => getOps(tx));
340343
return jsonFetch(`${this.config.apiURI}/admin/transact`, {
341344
method: 'POST',
342345
headers: authorizedHeaders(this.config, this.impersonationOpts),
343-
body: JSON.stringify({ steps: steps }),
346+
body: JSON.stringify({ steps: steps(inputChunks) }),
344347
});
345348
};
346349

@@ -367,11 +370,15 @@ class InstantAdmin<
367370
*/
368371
debugQuery = async <Q extends Query>(
369372
query: Exactly<Query, Q>,
370-
opts?: { rules: any },
373+
opts?: { rules?: any; ruleParams?: { [key: string]: any } },
371374
): Promise<{
372375
result: QueryResponse<Q, Schema, WithCardinalityInference>;
373376
checkResults: DebugCheckResult[];
374377
}> => {
378+
if (query && opts && 'ruleParams' in opts) {
379+
query = { $$ruleParams: opts['ruleParams'], ...query };
380+
}
381+
375382
const response = await jsonFetch(
376383
`${this.config.apiURI}/admin/query_perms_check`,
377384
{
@@ -409,13 +416,11 @@ class InstantAdmin<
409416
inputChunks: TransactionChunk<any, any> | TransactionChunk<any, any>[],
410417
opts?: { rules?: any },
411418
) => {
412-
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
413-
const steps = chunks.flatMap((tx) => getOps(tx));
414419
return jsonFetch(`${this.config.apiURI}/admin/transact_perms_check`, {
415420
method: 'POST',
416421
headers: authorizedHeaders(this.config, this.impersonationOpts),
417422
body: JSON.stringify({
418-
steps: steps,
423+
steps: steps(inputChunks),
419424
'rules-override': opts?.rules,
420425
// @ts-expect-error because we're using a private API (for now)
421426
'dangerously-commit-tx': opts?.__dangerouslyCommit,
@@ -856,13 +861,11 @@ class InstantAdminDatabase<Schema extends InstantSchemaDef<any, any, any>> {
856861
transact = (
857862
inputChunks: TransactionChunk<any, any> | TransactionChunk<any, any>[],
858863
) => {
859-
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
860-
const steps = chunks.flatMap((tx) => getOps(tx));
861864
return jsonFetch(`${this.config.apiURI}/admin/transact`, {
862865
method: 'POST',
863866
headers: authorizedHeaders(this.config, this.impersonationOpts),
864867
body: JSON.stringify({
865-
steps: steps,
868+
steps: steps(inputChunks),
866869
'throw-on-missing-attrs?': !!this.config.schema,
867870
}),
868871
});
@@ -891,11 +894,15 @@ class InstantAdminDatabase<Schema extends InstantSchemaDef<any, any, any>> {
891894
*/
892895
debugQuery = async <Q extends InstaQLParams<Schema>>(
893896
query: Q,
894-
opts?: { rules: any },
897+
opts?: { rules?: any; ruleParams?: { [key: string]: any } },
895898
): Promise<{
896899
result: InstaQLResponse<Schema, Q>;
897900
checkResults: DebugCheckResult[];
898901
}> => {
902+
if (query && opts && 'ruleParams' in opts) {
903+
query = { $$ruleParams: opts['ruleParams'], ...query };
904+
}
905+
899906
const response = await jsonFetch(
900907
`${this.config.apiURI}/admin/query_perms_check`,
901908
{
@@ -933,13 +940,11 @@ class InstantAdminDatabase<Schema extends InstantSchemaDef<any, any, any>> {
933940
inputChunks: TransactionChunk<any, any> | TransactionChunk<any, any>[],
934941
opts?: { rules?: any },
935942
) => {
936-
const chunks = Array.isArray(inputChunks) ? inputChunks : [inputChunks];
937-
const steps = chunks.flatMap((tx) => getOps(tx));
938943
return jsonFetch(`${this.config.apiURI}/admin/transact_perms_check`, {
939944
method: 'POST',
940945
headers: authorizedHeaders(this.config, this.impersonationOpts),
941946
body: JSON.stringify({
942-
steps: steps,
947+
steps: steps(inputChunks),
943948
'rules-override': opts?.rules,
944949
// @ts-expect-error because we're using a private API (for now)
945950
'dangerously-commit-tx': opts?.__dangerouslyCommit,

client/packages/cli/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"scripts": {
2727
"test": "vitest",
2828
"build": "rm -rf dist; tsc -p tsconfig.json",
29-
"dev": "tsc -p tsconfig.json -w --skipLibCheck",
29+
"dev": "tsc -p tsconfig.json -w --skipLibCheck --preserveWatchOutput",
3030
"publish-package": "pnpm publish --access public --no-git-checks"
3131
},
3232
"devDependencies": {

client/packages/core/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"bench:ci": "vitest bench --run",
1414
"check": "tsc --noEmit",
1515
"build": "rm -rf dist; npm run build:main && npm run build:module && npm run build:standalone",
16-
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck",
17-
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck",
16+
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck --preserveWatchOutput",
17+
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck --preserveWatchOutput",
1818
"dev": "run-p dev:main dev:module",
1919
"build:main": "tsc -p tsconfig.json",
2020
"build:module": "tsc -p tsconfig.module.json",

client/packages/core/src/Reactor.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,11 @@ export default class Reactor {
646646
*
647647
* Returns an unsubscribe function
648648
*/
649-
subscribeQuery(q, cb) {
649+
subscribeQuery(q, cb, opts) {
650+
if (opts && 'ruleParams' in opts) {
651+
q = { $$ruleParams: opts['ruleParams'], ...q };
652+
}
653+
650654
const hash = weakHash(q);
651655

652656
const prevResult = this.getPreviousResult(q);
@@ -664,7 +668,11 @@ export default class Reactor {
664668
};
665669
}
666670

667-
queryOnce(q) {
671+
queryOnce(q, opts) {
672+
if (opts && 'ruleParams' in opts) {
673+
q = { $$ruleParams: opts['ruleParams'], ...q };
674+
}
675+
668676
const dfd = new Deferred();
669677

670678
if (!this._isOnline) {
@@ -936,7 +944,7 @@ export default class Reactor {
936944
const mutation = {
937945
op: 'transact',
938946
'tx-steps': txSteps,
939-
error,
947+
error: error,
940948
};
941949
this.pendingMutations.set((prev) => {
942950
prev.set(eventId, mutation);

client/packages/core/src/index.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import type {
3535
Exactly,
3636
InstantObject,
3737
InstaQLParams,
38+
InstaQLOptions,
3839
InstaQLQueryParams,
3940
InstaQLEntity,
4041
InstaQLResult,
@@ -493,8 +494,9 @@ class InstantCoreDatabase<Schema extends InstantSchemaDef<any, any, any>>
493494
subscribeQuery<Q extends InstaQLParams<Schema>>(
494495
query: Q,
495496
cb: (resp: InstaQLSubscriptionState<Schema, Q>) => void,
497+
opts?: InstaQLOptions,
496498
) {
497-
return this._reactor.subscribeQuery(query, cb);
499+
return this._reactor.subscribeQuery(query, cb, opts);
498500
}
499501

500502
/**
@@ -614,11 +616,12 @@ class InstantCoreDatabase<Schema extends InstantSchemaDef<any, any, any>>
614616
*/
615617
queryOnce<Q extends InstaQLParams<Schema>>(
616618
query: Q,
619+
opts?: InstaQLOptions,
617620
): Promise<{
618621
data: InstaQLResponse<Schema, Q>;
619622
pageInfo: PageInfoResponse<Q>;
620623
}> {
621-
return this._reactor.queryOnce(query);
624+
return this._reactor.queryOnce(query, opts);
622625
}
623626
}
624627

@@ -782,6 +785,7 @@ export {
782785

783786
// new query types
784787
type InstaQLParams,
788+
type InstaQLOptions,
785789
type InstaQLQueryParams,
786790
type InstantQuery,
787791
type InstantQueryResult,

client/packages/core/src/instaml.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ function expandUnlink(attrs, [etype, eidA, obj]) {
175175
function expandUpdate(attrs, [etype, eid, obj]) {
176176
const lookup = extractLookup(attrs, etype, eid);
177177
// id first so that we don't clobber updates on the lookup field
178-
const attrTuples = [['id', extractLookup(attrs, etype, eid)]]
178+
const attrTuples = [['id', lookup]]
179179
.concat(Object.entries(obj))
180180
.map(([identName, value]) => {
181181
const attr = getAttrByFwdIdentName(attrs, etype, identName);
@@ -207,6 +207,12 @@ function expandDeepMerge(attrs, [etype, eid, obj]) {
207207
// id first so that we don't clobber updates on the lookup field
208208
return [idTuple].concat(attrTuples);
209209
}
210+
211+
function expandRuleParams(attrs, [etype, eid, ruleParams]) {
212+
const lookup = extractLookup(attrs, etype, eid);
213+
return [['rule-params', lookup, etype, ruleParams]];
214+
}
215+
210216
function removeIdFromArgs(step) {
211217
const [op, etype, eid, obj] = step;
212218
if (!obj) {
@@ -230,6 +236,8 @@ function toTxSteps(attrs, step) {
230236
return expandUnlink(attrs, args);
231237
case 'delete':
232238
return expandDelete(attrs, args);
239+
case 'ruleParams':
240+
return expandRuleParams(attrs, args);
233241
default:
234242
throw new Error(`unsupported action ${action}`);
235243
}
@@ -341,6 +349,7 @@ const SUPPORTS_LOOKUP_ACTIONS = new Set([
341349
'update',
342350
'merge',
343351
'delete',
352+
'ruleParams',
344353
]);
345354

346355
const lookupProps = { 'unique?': true, 'index?': true };

client/packages/core/src/instaql.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ function formatPageInfo(pageInfo) {
730730

731731
export default function query({ store, pageInfo, aggregate }, q) {
732732
const data = Object.keys(q).reduce(function reduceResult(res, k) {
733-
if (aggregate?.[k]) {
733+
if (aggregate?.[k] || '$$ruleParams' === k) {
734734
// Aggregate doesn't return any join rows and has no children,
735735
// so don't bother querying further
736736
return res;

client/packages/core/src/instatx.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import type {
22
IContainEntitiesAndLinks,
33
LinkParams,
44
UpdateParams,
5+
RuleParams,
56
} from './schemaTypes';
67

7-
type Action = 'update' | 'link' | 'unlink' | 'delete' | 'merge';
8+
type Action = 'update' | 'link' | 'unlink' | 'delete' | 'merge' | 'ruleParams';
89
type EType = string;
910
type Id = string;
1011
type Args = any;
@@ -96,6 +97,8 @@ export interface TransactionChunk<
9697
merge: (args: {
9798
[attribute: string]: any;
9899
}) => TransactionChunk<Schema, EntityName>;
100+
101+
ruleParams: (args: RuleParams) => TransactionChunk<Schema, EntityName>;
99102
}
100103

101104
export interface ETypeChunk<

client/packages/core/src/queryTypes.ts

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
IContainEntitiesAndLinks,
77
InstantGraph,
88
LinkAttrDef,
9+
RuleParams,
910
ResolveAttrs,
1011
ResolveEntityAttrs,
1112
} from './schemaTypes';
@@ -350,6 +351,10 @@ type InstaQLParams<S extends IContainEntitiesAndLinks<any, any>> = {
350351
type InstaQLQueryParams<S extends IContainEntitiesAndLinks<any, any>> =
351352
InstaQLParams<S>;
352353

354+
type InstaQLOptions = {
355+
ruleParams: RuleParams;
356+
};
357+
353358
export {
354359
Query,
355360
QueryResponse,
@@ -360,6 +365,7 @@ export {
360365
Remove$,
361366
InstaQLQueryResult,
362367
InstaQLParams,
368+
InstaQLOptions,
363369
InstaQLQueryEntityResult,
364370
InstaQLEntity,
365371
InstaQLResult,

client/packages/core/src/schemaTypes.ts

+4
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,7 @@ export type LinkParams<
503503
: string | string[]
504504
: never;
505505
};
506+
507+
export type RuleParams = {
508+
[key: string]: any;
509+
};

client/packages/core/vite.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
22
import { resolve } from 'path';
33

44
export default defineConfig({
5+
clearScreen: false,
56
build: {
67
outDir: 'dist/standalone',
78
lib: {

client/packages/react-native/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"scripts": {
1010
"test": "vitest",
1111
"build": "rm -rf dist; npm run build:main && npm run build:module",
12-
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck",
13-
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck",
12+
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck --preserveWatchOutput",
13+
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck --preserveWatchOutput",
1414
"dev": "run-p dev:main dev:module",
1515
"build:main": "tsc -p tsconfig.json",
1616
"build:module": "tsc -p tsconfig.module.json",

client/packages/react/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"scripts": {
1010
"test": "vitest",
1111
"build": "rm -rf dist; npm run build:main && npm run build:module && npm run build:standalone",
12-
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck",
13-
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck",
12+
"dev:main": "tsc -p tsconfig.json -w --skipLibCheck --preserveWatchOutput",
13+
"dev:module": "tsc -p tsconfig.module.json -w --skipLibCheck --preserveWatchOutput",
1414
"dev": "run-p dev:main dev:module",
1515
"build:main": "tsc -p tsconfig.json",
1616
"build:module": "tsc -p tsconfig.module.json",

client/packages/react/src/InstantReactAbstractDatabase.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
type PresenceResponse,
1212
type RoomSchemaShape,
1313
type InstaQLParams,
14+
type InstaQLOptions,
1415
type InstantConfig,
1516
type PageInfoResponse,
1617
InstantCoreDatabase,
@@ -155,8 +156,9 @@ export default abstract class InstantReactAbstractDatabase<
155156
*/
156157
useQuery = <Q extends InstaQLParams<Schema>>(
157158
query: null | Q,
159+
opts?: InstaQLOptions,
158160
): InstaQLLifecycleState<Schema, Q> => {
159-
return useQueryInternal(this._core, query).state;
161+
return useQueryInternal(this._core, query, opts).state;
160162
};
161163

162164
/**

0 commit comments

Comments
 (0)