Skip to content

Commit 7f66bc4

Browse files
author
rain
committed
Merge remote-tracking branch 'origin/master' into bot/docs-audit
2 parents ff9429e + 3c87e69 commit 7f66bc4

19 files changed

Lines changed: 1061 additions & 99 deletions

File tree

.github/workflows/release.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ on:
3838
required: true
3939
type: boolean
4040
default: false
41+
release_github:
42+
description: "Publish GitHub release (if nothing fails)"
43+
required: true
44+
type: boolean
45+
default: false
4146
update_mirror_latest_version:
4247
description: "Update S3 mirror latest-version marker"
4348
required: true
@@ -387,3 +392,46 @@ jobs:
387392
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
388393
AWS_DEFAULT_REGION: us-east-1
389394
run: aws s3 cp latest-version s3://spacetimedb-client-binaries/latest-version --acl public-read
395+
396+
publish-github-release:
397+
needs:
398+
- build-cargo-release
399+
- release-crates
400+
- release-csharp
401+
- release-cpp
402+
- release-npm
403+
- release-docker
404+
- update-mirror-latest-version
405+
runs-on: ubuntu-latest
406+
if: >-
407+
${{
408+
always()
409+
&& !inputs.dry_run
410+
&& inputs.release_github
411+
&& needs.build-cargo-release.result == 'success'
412+
&& contains(fromJSON('["success", "skipped"]'), needs.release-crates.result)
413+
&& contains(fromJSON('["success", "skipped"]'), needs.release-csharp.result)
414+
&& contains(fromJSON('["success", "skipped"]'), needs.release-cpp.result)
415+
&& contains(fromJSON('["success", "skipped"]'), needs.release-npm.result)
416+
&& contains(fromJSON('["success", "skipped"]'), needs.release-docker.result)
417+
&& contains(fromJSON('["success", "skipped"]'), needs.update-mirror-latest-version.result)
418+
}}
419+
permissions:
420+
actions: write
421+
contents: write
422+
env:
423+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
424+
steps:
425+
- name: Download cargo-release
426+
uses: actions/download-artifact@v4
427+
with:
428+
name: cargo-release-bin
429+
path: ./shared-bin
430+
431+
- name: Make binary executable and on PATH
432+
run: |
433+
chmod +x ./shared-bin/cargo-release
434+
echo "$PWD/shared-bin" >> "$GITHUB_PATH"
435+
436+
- name: Publish GitHub release
437+
run: cargo-release release github-release ${{ github.event.inputs.release_tag }}

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bindings-typescript/src/lib/table.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type {
2020
UntypedIndex,
2121
} from './indexes';
2222
import ScheduleAt from './schedule_at';
23-
import type { TableSchema } from './table_schema';
23+
import type { TableSchema, TableSchedule } from './table_schema';
2424
import {
2525
RowBuilder,
2626
type ColumnBuilder,
@@ -194,6 +194,11 @@ export type TableOpts<Row extends RowObj> = {
194194
public?: boolean;
195195
indexes?: IndexOpts<keyof Row & string>[]; // declarative multi‑column indexes
196196
constraints?: ConstraintOpts<keyof Row & string>[];
197+
/**
198+
* @deprecated Prefer `spacetime.reducer({ onSchedule: table }, ...)` or
199+
* `spacetime.procedure({ onSchedule: table }, ...)` so table definitions can
200+
* live in a separate module from reducer/procedure definitions.
201+
*/
197202
scheduled?: () =>
198203
| ReducerExport<any, { [k: string]: RowBuilder<RowObj> }>
199204
| ProcedureExport<
@@ -422,11 +427,9 @@ export function table<Row extends RowObj, const Opts extends TableOpts<Row>>(
422427
}
423428

424429
// If this column is shaped like ScheduleAtAlgebraicType, mark it as the schedule‑at column
425-
if (scheduled) {
426-
const algebraicType = builder.typeBuilder.algebraicType;
427-
if (ScheduleAt.isScheduleAt(algebraicType)) {
428-
scheduleAtCol = colIds.get(name)!;
429-
}
430+
const algebraicType = builder.typeBuilder.algebraicType;
431+
if (ScheduleAt.isScheduleAt(algebraicType)) {
432+
scheduleAtCol = colIds.get(name)!;
430433
}
431434
}
432435

@@ -492,7 +495,7 @@ export function table<Row extends RowObj, const Opts extends TableOpts<Row>>(
492495
CoerceRow<Row>
493496
>['algebraicType']['value'];
494497

495-
const schedule =
498+
const schedule: TableSchedule | undefined =
496499
scheduled && scheduleAtCol !== undefined
497500
? { scheduleAtCol, reducer: scheduled }
498501
: undefined;
@@ -543,6 +546,7 @@ export function table<Row extends RowObj, const Opts extends TableOpts<Row>>(
543546
// can expose them without type-smuggling.
544547
idxs: userIndexes as OptsIndices<Opts>,
545548
constraints: constraints as OptsConstraints<Opts>,
549+
scheduleAtCol,
546550
schedule,
547551
};
548552
}

crates/bindings-typescript/src/lib/table_schema.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
1-
import type { ProcedureExport, ReducerExport } from '../server';
21
import type { ProductType } from './algebraic_type';
32
import type { RawScheduleDefV10, RawTableDefV10 } from './autogen/types';
43
import type { IndexOpts } from './indexes';
54
import type { ModuleContext } from './schema';
65
import type { ColumnBuilder, RowBuilder } from './type_builders';
6+
import type { HasExactlyOneKnownKey } from './type_util';
7+
import type { ProcedureExport, ReducerExport } from '../server';
8+
9+
/**
10+
* Internal erased form of a scheduled reducer/procedure export.
11+
*
12+
* The legacy `TableOpts.scheduled` option checks the scheduled function shape
13+
* before it reaches `TableSchema`. From here, schedule resolution only needs
14+
* the export object identity to look up its registered function name.
15+
*/
16+
export type UntypedScheduledFunctionExport =
17+
| ReducerExport<any, any>
18+
| ProcedureExport<any, any, any>;
19+
20+
export type TableSchedule = {
21+
scheduleAtCol: number;
22+
reducer: () => UntypedScheduledFunctionExport;
23+
};
24+
25+
export type ScheduleTableForParams<Params extends Record<string, any>> =
26+
HasExactlyOneKnownKey<Params> extends true
27+
? Params[keyof Params] extends RowBuilder<
28+
infer Row extends Record<string, ColumnBuilder<any, any, any>>
29+
>
30+
? TableSchema<Row, readonly IndexOpts<keyof Row & string>[]>
31+
: never
32+
: never;
733

834
/**
935
* Represents a handle to a database table, including its name, row type, and row spacetime type.
@@ -50,12 +76,18 @@ export type TableSchema<
5076
}[];
5177

5278
/**
53-
* The schedule defined on the table, if any.
79+
* The column id of the schedule-at column, if this table has a ScheduleAt column.
80+
*/
81+
readonly scheduleAtCol?: number;
82+
83+
/**
84+
* The legacy schedule defined on the table, if any.
85+
*
86+
* @deprecated Prefer `spacetime.reducer({ onSchedule: table }, ...)` or
87+
* `spacetime.procedure({ onSchedule: table }, ...)` so table definitions can
88+
* live in a separate module from reducer/procedure definitions.
5489
*/
55-
readonly schedule?: {
56-
scheduleAtCol: number;
57-
reducer: () => ReducerExport<any, any> | ProcedureExport<any, any, any>;
58-
};
90+
readonly schedule?: TableSchedule;
5991
};
6092

6193
export type UntypedTableSchema = TableSchema<

crates/bindings-typescript/src/lib/type_util.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,31 @@ export type Values<T> = T[keyof T];
4848
*/
4949
export type CollapseTuple<A extends any[]> = A extends [infer T] ? T : A;
5050

51+
/**
52+
* Conditional-type helper for distinguishing a single type from a union.
53+
*/
54+
export type IsUnion<T, U = T> = [T] extends [never]
55+
? false
56+
: T extends any
57+
? [U] extends [T]
58+
? false
59+
: true
60+
: false;
61+
62+
/**
63+
* True when an object type has exactly one known key.
64+
*
65+
* If keys widen to plain `string`, the exact count is no longer knowable, so
66+
* treat it as valid and let narrower call sites or runtime checks handle it.
67+
*/
68+
export type HasExactlyOneKnownKey<T> = [keyof T] extends [never]
69+
? false
70+
: string extends keyof T
71+
? true
72+
: IsUnion<keyof T> extends true
73+
? false
74+
: true;
75+
5176
type CamelCaseImpl<S extends string> = S extends `${infer Head}_${infer Tail}`
5277
? `${Head}${Capitalize<CamelCaseImpl<Tail>>}`
5378
: S extends `${infer Head}-${infer Tail}`

crates/bindings-typescript/src/server/procedures.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import type { ConnectionId } from '../lib/connection_id';
1111
import { Identity } from '../lib/identity';
1212
import type { ParamsObj, ReducerCtx } from '../lib/reducers';
1313
import { type UntypedSchemaDef } from '../lib/schema';
14+
import type { ScheduleTableForParams } from '../lib/table_schema';
1415
import { Timestamp } from '../lib/timestamp';
1516
import {
1617
type Infer,
1718
type InferTypeOfRow,
19+
type t,
1820
type TypeBuilder,
1921
} from '../lib/type_builders';
2022
import { bsatnBaseSize } from '../lib/util';
@@ -42,7 +44,7 @@ export function makeProcedureExport<
4244
Ret extends TypeBuilder<any, any>,
4345
>(
4446
ctx: SchemaInner,
45-
opts: ProcedureOpts | undefined,
47+
opts: ProcedureOptsWithOptionalName<Params, Ret> | undefined,
4648
params: Params,
4749
ret: Ret,
4850
fn: ProcedureFn<S, Params, Ret>
@@ -58,6 +60,12 @@ export function makeProcedureExport<
5860
procedureExport as ProcedureExport<any, any, any>,
5961
name ?? exportName
6062
);
63+
if (opts?.onSchedule !== undefined) {
64+
ctx.pendingSchedules.push({
65+
table: opts.onSchedule,
66+
functionName: name ?? exportName,
67+
});
68+
}
6169
};
6270

6371
return procedureExport;
@@ -69,10 +77,21 @@ export type ProcedureFn<
6977
Ret extends TypeBuilder<any, any>,
7078
> = (ctx: ProcedureCtx<S>, args: InferTypeOfRow<Params>) => Infer<Ret>;
7179

72-
export interface ProcedureOpts {
80+
export interface ProcedureOpts<
81+
Params extends ParamsObj = ParamsObj,
82+
Ret extends TypeBuilder<any, any> = TypeBuilder<any, any>,
83+
> {
7384
name: string;
85+
onSchedule?: Ret extends ReturnType<typeof t.unit>
86+
? ScheduleTableForParams<Params>
87+
: never;
7488
}
7589

90+
export type ProcedureOptsWithOptionalName<
91+
Params extends ParamsObj = ParamsObj,
92+
Ret extends TypeBuilder<any, any> = TypeBuilder<any, any>,
93+
> = Omit<ProcedureOpts<Params, Ret>, 'name'> & { name?: string };
94+
7695
export interface ProcedureCtx<S extends UntypedSchemaDef> {
7796
readonly sender: Identity;
7897
readonly databaseIdentity: Identity;
@@ -107,7 +126,7 @@ function registerProcedure<
107126
params: Params,
108127
ret: Ret,
109128
fn: ProcedureFn<S, Params, Ret>,
110-
opts?: ProcedureOpts
129+
opts?: ProcedureOptsWithOptionalName<any, any>
111130
) {
112131
ctx.defineFunction(exportName);
113132
const paramsType: ProductType = {

crates/bindings-typescript/src/server/reducers.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { AlgebraicType } from '../lib/algebraic_type';
22
import { FunctionVisibility, type Lifecycle } from '../lib/autogen/types';
33
import type { ParamsObj, Reducer } from '../lib/reducers';
44
import { type UntypedSchemaDef } from '../lib/schema';
5+
import type { ScheduleTableForParams } from '../lib/table_schema';
56
import { RowBuilder, type RowObj } from '../lib/type_builders';
67
import { toPascalCase } from '../lib/util';
78
import {
@@ -17,16 +18,20 @@ export interface ReducerExport<
1718
> extends Reducer<S, Params>,
1819
ModuleExport {}
1920

20-
export interface ReducerOpts {
21+
export interface ReducerOpts<Params extends ParamsObj = ParamsObj> {
2122
name: string;
23+
onSchedule?: ScheduleTableForParams<Params>;
2224
}
2325

26+
export type ReducerOptsWithOptionalName<Params extends ParamsObj = ParamsObj> =
27+
Omit<ReducerOpts<Params>, 'name'> & { name?: string };
28+
2429
export function makeReducerExport<
2530
S extends UntypedSchemaDef,
2631
Params extends ParamsObj,
2732
>(
2833
ctx: SchemaInner,
29-
opts: ReducerOpts | undefined,
34+
opts: ReducerOptsWithOptionalName<Params> | undefined,
3035
params: RowObj | RowBuilder<RowObj>,
3136
fn: Reducer<any, any>,
3237
lifecycle?: Lifecycle
@@ -39,6 +44,12 @@ export function makeReducerExport<
3944
reducerExport as ReducerExport<any, any>,
4045
exportName
4146
);
47+
if (opts?.onSchedule !== undefined) {
48+
ctx.pendingSchedules.push({
49+
table: opts.onSchedule,
50+
functionName: exportName,
51+
});
52+
}
4253
};
4354

4455
return reducerExport;
@@ -57,7 +68,7 @@ export function registerReducer(
5768
exportName: string,
5869
params: RowObj | RowBuilder<RowObj>,
5970
fn: Reducer<any, any>,
60-
opts?: ReducerOpts,
71+
opts?: ReducerOptsWithOptionalName<any>,
6172
lifecycle?: Lifecycle
6273
): void {
6374
ctx.defineFunction(exportName);

0 commit comments

Comments
 (0)