Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e4b4222
Add the design for module scripts in their crates
jwrosewell Oct 19, 2026
338b648
Move module browser scripts into their crates
jwrosewell Oct 19, 2026
b895571
Move the Tinybird project into its crate
jwrosewell Oct 19, 2026
0e0d3aa
Apply the second review's corrections
jwrosewell Oct 19, 2026
b08a8ff
Name the design's pull request
jwrosewell Oct 20, 2026
5be1147
Test module discovery without a file system
jwrosewell Oct 20, 2026
32d7485
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
104fb22
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
4d3daca
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
306046b
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
63fa9db
The scripts design's review log cites no unpublished commit
jwrosewell Oct 28, 2026
ff975c1
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
10fca21
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
8c64bdd
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
7f232e6
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
6e9d0f3
Row 55's design copy names no local branch
jwrosewell Oct 28, 2026
e4e721d
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
56609c8
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
1958b97
Merge split/54-asset-routes into split/55-scripts-in-crates
jwrosewell Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ jobs:
working-directory: .
run: >-
docs/node_modules/.bin/prettier --config docs/.prettierrc --check
"*.md" ".claude/**/*.md" ".github/**/*.md" "crates/**/*.md" "scripts/**/*.md" "tinybird/**/*.md"
"*.md" ".claude/**/*.md" ".github/**/*.md" "crates/**/*.md" "scripts/**/*.md"

- name: Build with VitePress (fails on dead links)
run: npm run build
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ IntegrationRegistration::builder(ID)

## JS Build Pipeline

- `build-all.mjs` discovers `src/integrations/*/index.ts` and builds each as a separate IIFE.
- `build-all.mjs` discovers `src/integrations/*/index.ts` and `crates/<type>/<crate>/js/src/*/index.ts`, and builds each as a separate IIFE.
- Output: `dist/tsjs-core.js`, `dist/tsjs-{integration}.js` from `npm run build`. `build.rs` passes `--out-dir` to build the same files into a private `OUT_DIR/tsjs-dist`, validates the set, and never reads `dist/`.
- `build.rs` auto-generates `tsjs_modules.rs` with `include_str!()` for each discovered file.
- `bundle.rs` provides `concatenate_modules(ids)` and `concatenated_hash(ids)` APIs.
Expand Down Expand Up @@ -492,7 +492,7 @@ Every PR must pass:
5. JS build and test (`cd crates/trusted-server-js/lib && npx vitest run`)
6. JS format (`cd crates/trusted-server-js/lib && npm run format`)
7. Docs format (`cd docs && npm run format`)
8. Markdown format outside `docs/` (requires `cd docs && npm ci` first): `docs/node_modules/.bin/prettier --config docs/.prettierrc --check "*.md" ".claude/**/*.md" ".github/**/*.md" "crates/**/*.md" "scripts/**/*.md" "tinybird/**/*.md"`; fix with `--write` in place of `--check`
8. Markdown format outside `docs/` (requires `cd docs && npm ci` first): `docs/node_modules/.bin/prettier --config docs/.prettierrc --check "*.md" ".claude/**/*.md" ".github/**/*.md" "crates/**/*.md" "scripts/**/*.md"`; fix with `--write` in place of `--check`
9. Native core build-digest test and lint (`cargo test -p trusted-server-core --test template_build_digest` and `cargo clippy -p trusted-server-core --test template_build_digest -- -D warnings`)
10. Wasm clock lint: `scripts/lint-wasm-clock.sh` (crates built into the Cloudflare Worker must use `web_time`, not the `std::time::{Instant,SystemTime}` `now`/`elapsed` methods, which panic there)
11. Documentation proposal scripts: `shellcheck scripts/docs-proposal/*.sh && node --test scripts/docs-proposal/hunks.test.mjs && scripts/docs-proposal/test.sh`
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- The Tinybird project, its datasources, pipes, fixtures and tests, moves from `tinybird/` to `crates/analytics/tinybird/project/`, beside the analytics module's Rust, so run `tb` from the new folder.
- A vendor module's browser script and its tests live in the crate that supplies the module, under `js/src/<module>/` and `js/test/<module>/`, where they were under `crates/trusted-server-js/lib/src/integrations/` and `lib/test/integrations/`, for DataDome, Didomi, Google Tag Manager, GPT, GPT diagnostics, Lockr, Osano, Permutive, Sourcepoint and Testlight. The built bundles are unchanged. A script in a crate imports the shared browser code as `@tsjs/core/...` and `@tsjs/shared/...`, and another module's code as `@tsjs/module/<module>/...`.
- The Axum, Cloudflare and Spin adapters serve `[[proxy.asset_routes]]`, where only the Fastly adapter did, so a `GET` or `HEAD` under a route's prefix is answered from the route's source, with its signing and with the answers a selected asset cache module holds for it, and no longer by the publisher's origin. The response skips the Edge Cookie steps, so it neither writes nor expires the Edge Cookie, and takes the route's cache policy after the finalize headers. Those adapters hold the asset body whole, up to 10 MiB on Cloudflare and 8 MiB on Spin. Their HTTP clients refuse Image Optimizer metadata, which the Axum client now does on both of its send paths, so there a route that enables the Image Optimizer answers each request it would optimize with `502` and `no-store, private`, after a signed route has asked its origin for the object with a `HEAD`, and serves its SVG paths and requests that set the debug parameter unoptimized. Each adapter logs a warning naming every such route, once for each instance of the adapter, which on Axum is the process.
- **Breaking:** on Spin, a request to a source whose `host_header` names a host other than the one in its `url` answers `502` before its origin is asked, the publisher's `[source.origin]` as well as an asset route's source, because Spin sends the host in `url` as the `Host`. Before, the request reached its origin with the host in `url` in place of `host_header`. A Spin deployment whose publisher source sets such a `host_header` answers every page with `502`, robots.txt included. Before upgrading, check that no source of a Spin deployment sets a `host_header` naming another host, or remove it once its origin answers requests that carry the host in `url` as the `Host`.
- The Edge Cookie is written without an identity graph as it is with one, because the graph is one optional store of the data kept with the cookie, such as partner IDs. A Fastly service with no `[ec] ec_store` now writes the cookie a real browser's page navigation creates, and `POST /_ts/api/v1/ec/resolve` answers there as it does with a graph, where neither wrote one. So the identifier auction providers get as `user.id`, where storage and personalized-ad selection are both set, is one value for the life of the cookie, where a server-side module such as `hmac` gave a new value on each page view and a client-side module gave none. Naming an `ec_store` later gives each browser holding a cookie a server-side module wrote without one a new identifier on its first page view, because the identity graph replaces an identifier it has no row for. A client-side module creates nothing at the edge to replace its cookie with, so that cookie keeps its identifier with no row for as long as the browser keeps the cookie and its resolved marker, and each of that browser's page views pays for an extra identity-graph read and a strongly consistent existence check. **Breaking for crates outside core:** `ec_finalize_response` and `handle_ec_resolve` read the bot gate from the context's device signals (`EcContext::set_device_signals`), so a context with none gets no created cookie.
Expand Down
13 changes: 13 additions & 0 deletions crates/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"overrides": [
{
"files": ["*/*/js/{src,test}/**", ".prettierrc.json", "eslint.config.js", "tsconfig.json"],
"options": {
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import {
publisherFirstImpressionRetryDelay,
releaseTrustedServerFirstImpressionClaim,
reservePublisherFirstImpressionFallback,
} from '../../core/first_impression';
import { log } from '../../core/log';
import { resolveSlotElementByDivId } from '../../core/slot_element';
} from '@tsjs/core/first_impression';
import { log } from '@tsjs/core/log';
import { resolveSlotElementByDivId } from '@tsjs/core/slot_element';
import type {
AuctionSlot,
AuctionBidData,
GptDiagnosticsCreativeFailure,
GptDiagnosticsTrustedServerOpportunity,
GptSlotHandoff,
TsjsApi,
} from '../../core/types';
import { viewportBandSizes } from '../../core/viewport_sizes';
} from '@tsjs/core/types';
import { viewportBandSizes } from '@tsjs/core/viewport_sizes';
import {
APS_UNIVERSAL_CREATIVE_RENDERER,
APS_UNIVERSAL_CREATIVE_RENDERER_VERSION,
Expand All @@ -25,7 +25,7 @@ import {
consumeApsPrebidRenderer,
getApsPrebidRenderer,
validateApsRenderer,
} from '../aps/render';
} from '@tsjs/module/aps/render';

import { installGptGuard } from './script_guard';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { log } from '../../core/log';
import { log } from '@tsjs/core/log';
import {
DEFAULT_DOM_INSERTION_HANDLER_PRIORITY,
type DomInsertionCandidate,
registerDomInsertionHandler,
} from '../../shared/dom_insertion_dispatcher';
} from '@tsjs/shared/dom_insertion_dispatcher';

/**
* GPT Script Interception Guard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
GptDiagnosticsRecorder,
GptDiagnosticsSlotHandle,
GptDiagnosticsTrustedServerOpportunity,
} from '../../core/types';
} from '@tsjs/core/types';

import type { GptDiagnosticsBindingManager } from './binding';
import type { GptDiagnosticsStoreSnapshot } from './store';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GptDiagnosticsRequestCycle } from '../../core/types';
import type { GptDiagnosticsRequestCycle } from '@tsjs/core/types';

import type { GptDiagnosticsBindingManager } from './binding';
import { unhandledCase } from './exhaustive';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GptDiagnosticsBinding, GptDiagnosticsSlotExport } from '../../core/types';
import type { GptDiagnosticsBinding, GptDiagnosticsSlotExport } from '@tsjs/core/types';

import { scheduleFrame } from './presentation_helpers';
import type { GptDiagnosticsBindingInput } from './store';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { log } from '../../core/log';
import type { GptDiagnosticsApi, TsjsApi } from '../../core/types';
import { log } from '@tsjs/core/log';
import type { GptDiagnosticsApi, TsjsApi } from '@tsjs/core/types';

import { GptDiagnosticsApiController } from './api';
import { GptDiagnosticsBadgeManager } from './badges';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { log } from '../../core/log';
import type { GptDiagnosticsAdManagerIdentity, Size, TsjsApi } from '../../core/types';
import { log } from '@tsjs/core/log';
import type { GptDiagnosticsAdManagerIdentity, Size, TsjsApi } from '@tsjs/core/types';

import type { GptDiagnosticsSlotLike, GptRenderFacts } from './store';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { log } from '../../core/log';
import type { GptDiagnosticsRequestCycle } from '../../core/types';
import { log } from '@tsjs/core/log';
import type { GptDiagnosticsRequestCycle } from '@tsjs/core/types';

import type { GptDiagnosticsBindingManager } from './binding';
import { unhandledCase } from './exhaustive';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Size } from '../../core/types';
import type { Size } from '@tsjs/core/types';

/** Formats CSS sizes consistently across diagnostics presentation surfaces. */
export function formatSizes(sizes: ReadonlyArray<Size>): string {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Size } from '../../core/types';
import type { Size } from '@tsjs/core/types';

import type { GptDiagnosticsBindingManager } from './binding';
import { scheduleFrame } from './presentation_helpers';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
GptDiagnosticsSlotHandle,
GptDiagnosticsTrustedServerOpportunity,
Size,
} from '../../core/types';
} from '@tsjs/core/types';

export const MAX_DIAGNOSTIC_SLOTS = 64;
export const MAX_REQUEST_CYCLES_PER_SLOT = 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,4 @@ import { resolve } from 'node:path';
* segment doubles. Resolving from this module's own directory also keeps the
* path independent of the working directory the suite is launched from.
*/
export const GPT_BOOTSTRAP_PATH = resolve(
import.meta.dirname,
'../../../../ad-tag/google/src/gpt_bootstrap.js'
);
export const GPT_BOOTSTRAP_PATH = resolve(import.meta.dirname, '../../../src/gpt_bootstrap.js');
Loading
Loading