@malloy-publisher/server 0.0.205 → 0.0.206
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/dist/app/api-doc.yaml +363 -395
- package/dist/app/assets/{EnvironmentPage-CAge6UHD.js → EnvironmentPage-BYwBeC2F.js} +1 -1
- package/dist/app/assets/{HomePage-DhTe8qpa.js → HomePage-ivu4vdpj.js} +1 -1
- package/dist/app/assets/{MainPage-CeTxxGex.js → MainPage-B2DnHEDU.js} +2 -2
- package/dist/app/assets/{MaterializationsPage-CpDHB70t.js → MaterializationsPage-BZEuwF9P.js} +1 -1
- package/dist/app/assets/{ModelPage-D9sSMb75.js → ModelPage-Dpu3bfPg.js} +1 -1
- package/dist/app/assets/{PackagePage-LRqQWrFY.js → PackagePage-B8PwcRHt.js} +1 -1
- package/dist/app/assets/{RouteError-xT6kuCNw.js → RouteError-BhbywAeC.js} +1 -1
- package/dist/app/assets/{WorkbookPage-DsIh9svZ.js → WorkbookPage-C-JXsJG0.js} +1 -1
- package/dist/app/assets/{core-C2sQrwVu.es-Bjem0hym.js → core-pPlPr7jK.es-CNEOlxKB.js} +1 -1
- package/dist/app/assets/{index-BdOZDcce.js → index-BHEm8Egc.js} +1 -1
- package/dist/app/assets/{index-DHHAcY5o.js → index-BsvDrV14.js} +1 -1
- package/dist/app/assets/{index-RX3QOTde.js → index-ChR1fKR2.js} +127 -127
- package/dist/app/assets/{index.umd-D2WH3D-f.js → index.umd-BVLPYNuj.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/instrumentation.mjs +18 -8
- package/dist/package_load_worker.mjs +19 -2
- package/dist/server.mjs +1151 -1264
- package/package.json +1 -1
- package/src/constants.ts +12 -0
- package/src/controller/materialization.controller.ts +79 -35
- package/src/controller/package.controller.spec.ts +179 -0
- package/src/controller/package.controller.ts +60 -73
- package/src/dto/package.dto.ts +16 -1
- package/src/errors.spec.ts +12 -0
- package/src/errors.ts +18 -0
- package/src/health.spec.ts +34 -1
- package/src/instrumentation.ts +33 -17
- package/src/materialization_metrics.ts +66 -0
- package/src/package_load/package_load_pool.ts +7 -1
- package/src/package_load/package_load_worker.ts +44 -4
- package/src/package_load/protocol.ts +7 -1
- package/src/server-old.ts +7 -149
- package/src/server.ts +9 -188
- package/src/service/authorize_integration.spec.ts +67 -0
- package/src/service/environment.ts +212 -12
- package/src/service/environment_store.spec.ts +0 -81
- package/src/service/environment_store.ts +0 -23
- package/src/service/explore_visibility.spec.ts +434 -0
- package/src/service/exports_probe.spec.ts +107 -0
- package/src/service/manifest_loader.spec.ts +99 -0
- package/src/service/manifest_loader.ts +95 -0
- package/src/service/materialization_service.spec.ts +324 -512
- package/src/service/materialization_service.ts +816 -656
- package/src/service/model.ts +400 -13
- package/src/service/package.spec.ts +14 -2
- package/src/service/package.ts +254 -14
- package/src/service/package_rollback.spec.ts +190 -0
- package/src/service/package_worker_path.spec.ts +223 -0
- package/src/service/query_boundary.spec.ts +470 -0
- package/src/storage/DatabaseInterface.ts +35 -57
- package/src/storage/StorageManager.mock.ts +0 -9
- package/src/storage/StorageManager.ts +7 -290
- package/src/storage/duckdb/DuckDBRepository.ts +2 -35
- package/src/storage/duckdb/MaterializationRepository.ts +52 -27
- package/src/storage/duckdb/schema.ts +4 -20
- package/tests/integration/materialization/manifest_binding.integration.spec.ts +175 -0
- package/tests/integration/materialization/materialization_lifecycle.integration.spec.ts +277 -266
- package/tests/unit/duckdb/legacy_schema_migration.test.ts +7 -4
- package/src/controller/manifest.controller.ts +0 -38
- package/src/service/manifest_service.spec.ts +0 -206
- package/src/service/manifest_service.ts +0 -117
- package/src/service/materialized_table_gc.spec.ts +0 -384
- package/src/service/materialized_table_gc.ts +0 -231
- package/src/storage/duckdb/DuckDBManifestStore.ts +0 -70
- package/src/storage/duckdb/ManifestRepository.ts +0 -120
- package/src/storage/duckdb/manifest_store.spec.ts +0 -133
- package/src/storage/ducklake/DuckLakeManifestStore.ts +0 -155
- package/tests/unit/storage/StorageManager.test.ts +0 -166
|
@@ -280,6 +280,221 @@ source: gated is duckdb.sql("select 1 as id")`,
|
|
|
280
280
|
}
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
+
it("rejects a package whose view carries an invalid renderer tag", async () => {
|
|
284
|
+
writeManifest();
|
|
285
|
+
// `# big_value { sparkline=... }` is a child-only renderer config placed on
|
|
286
|
+
// the view itself, with no activating big_value. The renderer declines to
|
|
287
|
+
// match it; without compile-time validation it renders as "[object Object]"
|
|
288
|
+
// at query time. Render-tag validation runs on the main thread after the
|
|
289
|
+
// worker hydrates the model, so the load must reject with a 424
|
|
290
|
+
// ModelCompilationError naming the offending view.
|
|
291
|
+
fs.writeFileSync(
|
|
292
|
+
path.join(tempDir, "bad_render.malloy"),
|
|
293
|
+
`source: nums is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
294
|
+
measure: total is a.sum()
|
|
295
|
+
# big_value { sparkline=trend }
|
|
296
|
+
view: card is {
|
|
297
|
+
aggregate: total
|
|
298
|
+
nest:
|
|
299
|
+
# line_chart { size=spark }
|
|
300
|
+
trend is { group_by: a; aggregate: total }
|
|
301
|
+
}
|
|
302
|
+
}`,
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const { ModelCompilationError } = await import("../errors");
|
|
306
|
+
const { malloyConfig, duckdb } = await makeMalloyConfig();
|
|
307
|
+
try {
|
|
308
|
+
let caught: unknown;
|
|
309
|
+
try {
|
|
310
|
+
await Package.create("env", "pkg", tempDir, malloyConfig);
|
|
311
|
+
} catch (err) {
|
|
312
|
+
caught = err;
|
|
313
|
+
}
|
|
314
|
+
expect(caught).toBeInstanceOf(ModelCompilationError);
|
|
315
|
+
expect((caught as Error).message).toContain(
|
|
316
|
+
"Invalid renderer configuration",
|
|
317
|
+
);
|
|
318
|
+
expect((caught as Error).message).toContain("nums -> card");
|
|
319
|
+
} finally {
|
|
320
|
+
await duckdb.close();
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("loads a package whose view renderer tags are valid", async () => {
|
|
325
|
+
writeManifest();
|
|
326
|
+
fs.writeFileSync(
|
|
327
|
+
path.join(tempDir, "good_render.malloy"),
|
|
328
|
+
`source: nums is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
329
|
+
measure: total is a.sum()
|
|
330
|
+
# bar_chart
|
|
331
|
+
view: chart is { group_by: a; aggregate: total }
|
|
332
|
+
}`,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const { malloyConfig, duckdb } = await makeMalloyConfig();
|
|
336
|
+
try {
|
|
337
|
+
const pkg = await Package.create("env", "pkg", tempDir, malloyConfig);
|
|
338
|
+
expect(pkg.getModelPaths()).toEqual(["good_render.malloy"]);
|
|
339
|
+
} finally {
|
|
340
|
+
await duckdb.close();
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("rejects a package whose backtick-quoted (hyphenated) source has a bad view render tag", async () => {
|
|
345
|
+
writeManifest();
|
|
346
|
+
// A source whose name needs Malloy backtick-quoting (here, a hyphen). The
|
|
347
|
+
// validation target must quote the identifier; otherwise `run: bad-source
|
|
348
|
+
// -> card` fails to lex, loadQuery throws, the catch swallows it, and the
|
|
349
|
+
// misconfigured view is silently skipped -- which would defeat the feature
|
|
350
|
+
// for any quote-needing name (the repo already has such a source,
|
|
351
|
+
// `gated-source`, in authorize_integration.spec.ts).
|
|
352
|
+
fs.writeFileSync(
|
|
353
|
+
path.join(tempDir, "hyphen_render.malloy"),
|
|
354
|
+
`source: \`bad-source\` is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
355
|
+
measure: total is a.sum()
|
|
356
|
+
# big_value { sparkline=trend }
|
|
357
|
+
view: card is {
|
|
358
|
+
aggregate: total
|
|
359
|
+
nest:
|
|
360
|
+
# line_chart { size=spark }
|
|
361
|
+
trend is { group_by: a; aggregate: total }
|
|
362
|
+
}
|
|
363
|
+
}`,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const { ModelCompilationError } = await import("../errors");
|
|
367
|
+
const { malloyConfig, duckdb } = await makeMalloyConfig();
|
|
368
|
+
try {
|
|
369
|
+
let caught: unknown;
|
|
370
|
+
try {
|
|
371
|
+
await Package.create("env", "pkg", tempDir, malloyConfig);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
caught = err;
|
|
374
|
+
}
|
|
375
|
+
expect(caught).toBeInstanceOf(ModelCompilationError);
|
|
376
|
+
expect((caught as Error).message).toContain(
|
|
377
|
+
"Invalid renderer configuration",
|
|
378
|
+
);
|
|
379
|
+
expect((caught as Error).message).toContain("bad-source -> card");
|
|
380
|
+
} finally {
|
|
381
|
+
await duckdb.close();
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("rejects a package whose source name contains an escaped backtick with a bad view render tag", async () => {
|
|
386
|
+
writeManifest();
|
|
387
|
+
// Source name with a literal backtick (written escaped in Malloy as
|
|
388
|
+
// `wei\`rd`). The validation target must re-escape it; a bare wrap would
|
|
389
|
+
// produce `run: `wei`rd` -> ...`, where the embedded backtick closes the
|
|
390
|
+
// quote early, fails to lex, and the bad view is silently skipped. Mirrors
|
|
391
|
+
// malloy's own identifierCode escaping.
|
|
392
|
+
fs.writeFileSync(
|
|
393
|
+
path.join(tempDir, "backtick_render.malloy"),
|
|
394
|
+
`source: \`wei\\\`rd\` is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
395
|
+
measure: total is a.sum()
|
|
396
|
+
# big_value { sparkline=trend }
|
|
397
|
+
view: card is {
|
|
398
|
+
aggregate: total
|
|
399
|
+
nest:
|
|
400
|
+
# line_chart { size=spark }
|
|
401
|
+
trend is { group_by: a; aggregate: total }
|
|
402
|
+
}
|
|
403
|
+
}`,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const { ModelCompilationError } = await import("../errors");
|
|
407
|
+
const { malloyConfig, duckdb } = await makeMalloyConfig();
|
|
408
|
+
try {
|
|
409
|
+
let caught: unknown;
|
|
410
|
+
try {
|
|
411
|
+
await Package.create("env", "pkg", tempDir, malloyConfig);
|
|
412
|
+
} catch (err) {
|
|
413
|
+
caught = err;
|
|
414
|
+
}
|
|
415
|
+
expect(caught).toBeInstanceOf(ModelCompilationError);
|
|
416
|
+
expect((caught as Error).message).toContain(
|
|
417
|
+
"Invalid renderer configuration",
|
|
418
|
+
);
|
|
419
|
+
expect((caught as Error).message).toContain("card");
|
|
420
|
+
} finally {
|
|
421
|
+
await duckdb.close();
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("rejects a .malloynb notebook whose source view carries an invalid renderer tag", async () => {
|
|
426
|
+
writeManifest();
|
|
427
|
+
fs.writeFileSync(
|
|
428
|
+
path.join(tempDir, "bad_render.malloynb"),
|
|
429
|
+
`>>>malloy
|
|
430
|
+
source: nums is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
431
|
+
measure: total is a.sum()
|
|
432
|
+
# big_value { sparkline=trend }
|
|
433
|
+
view: card is {
|
|
434
|
+
aggregate: total
|
|
435
|
+
nest:
|
|
436
|
+
# line_chart { size=spark }
|
|
437
|
+
trend is { group_by: a; aggregate: total }
|
|
438
|
+
}
|
|
439
|
+
}`,
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
const { ModelCompilationError } = await import("../errors");
|
|
443
|
+
const { malloyConfig, duckdb } = await makeMalloyConfig();
|
|
444
|
+
try {
|
|
445
|
+
await expect(
|
|
446
|
+
Package.create("env", "pkg", tempDir, malloyConfig),
|
|
447
|
+
).rejects.toBeInstanceOf(ModelCompilationError);
|
|
448
|
+
} finally {
|
|
449
|
+
await duckdb.close();
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("re-validates renderer tags on reload, recording a model that develops a bad tag as a per-model error", async () => {
|
|
454
|
+
writeManifest();
|
|
455
|
+
// Start valid so Package.create succeeds.
|
|
456
|
+
fs.writeFileSync(
|
|
457
|
+
path.join(tempDir, "m.malloy"),
|
|
458
|
+
`source: nums is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
459
|
+
measure: total is a.sum()
|
|
460
|
+
# bar_chart
|
|
461
|
+
view: chart is { group_by: a; aggregate: total }
|
|
462
|
+
}`,
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
const { malloyConfig, duckdb } = await makeMalloyConfig();
|
|
466
|
+
try {
|
|
467
|
+
const pkg = await Package.create("env", "pkg", tempDir, malloyConfig);
|
|
468
|
+
expect(pkg.getModel("m.malloy")).toBeDefined();
|
|
469
|
+
|
|
470
|
+
// The model now develops a misconfigured render tag. Reload must catch
|
|
471
|
+
// it (not just Package.create) and surface it as the model's error,
|
|
472
|
+
// without aborting the whole reload.
|
|
473
|
+
fs.writeFileSync(
|
|
474
|
+
path.join(tempDir, "m.malloy"),
|
|
475
|
+
`source: nums is duckdb.sql("select 1 as a, 2 as b") extend {
|
|
476
|
+
measure: total is a.sum()
|
|
477
|
+
# big_value { sparkline=trend }
|
|
478
|
+
view: card is {
|
|
479
|
+
aggregate: total
|
|
480
|
+
nest:
|
|
481
|
+
# line_chart { size=spark }
|
|
482
|
+
trend is { group_by: a; aggregate: total }
|
|
483
|
+
}
|
|
484
|
+
}`,
|
|
485
|
+
);
|
|
486
|
+
await pkg.reloadAllModels({});
|
|
487
|
+
|
|
488
|
+
const reloaded = pkg.getModel("m.malloy");
|
|
489
|
+
expect(reloaded).toBeDefined();
|
|
490
|
+
await expect(reloaded!.getModel()).rejects.toThrow(
|
|
491
|
+
"Invalid renderer configuration",
|
|
492
|
+
);
|
|
493
|
+
} finally {
|
|
494
|
+
await duckdb.close();
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
283
498
|
// NB: kept last in this describe — swapping the singleton for a
|
|
284
499
|
// pre-shutdown pool also tears down the shared `pool` (the swap
|
|
285
500
|
// implementation shuts down the outgoing singleton). Subsequent
|
|
@@ -304,6 +519,14 @@ source: gated is duckdb.sql("select 1 as id")`,
|
|
|
304
519
|
).rejects.toBeInstanceOf(ServiceUnavailableError);
|
|
305
520
|
} finally {
|
|
306
521
|
await duckdb.close();
|
|
522
|
+
// Replace the singleton with a fresh, live pool immediately. The
|
|
523
|
+
// outer afterAll also resets to null, but restoring here ensures the
|
|
524
|
+
// shut-down pool never outlives this test -- otherwise, under serial
|
|
525
|
+
// execution, a later spec file that lazily reuses the singleton via
|
|
526
|
+
// getPackageLoadPool() could observe the dead pool before afterAll
|
|
527
|
+
// runs and fail with "PackageLoadPool is shutting down".
|
|
528
|
+
pool = new PackageLoadPool(1);
|
|
529
|
+
await __setPackageLoadPoolForTests(pool);
|
|
307
530
|
}
|
|
308
531
|
});
|
|
309
532
|
});
|