was-teaching-server 0.9.1 → 0.11.0

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +118 -1
  2. package/README.md +23 -11
  3. package/dist/backends/filesystem.d.ts +247 -4
  4. package/dist/backends/filesystem.d.ts.map +1 -1
  5. package/dist/backends/filesystem.js +711 -81
  6. package/dist/backends/filesystem.js.map +1 -1
  7. package/dist/backends/postgres.d.ts +240 -2
  8. package/dist/backends/postgres.d.ts.map +1 -1
  9. package/dist/backends/postgres.js +800 -42
  10. package/dist/backends/postgres.js.map +1 -1
  11. package/dist/backends/postgresSchema.d.ts.map +1 -1
  12. package/dist/backends/postgresSchema.js +69 -57
  13. package/dist/backends/postgresSchema.js.map +1 -1
  14. package/dist/errors.d.ts +26 -9
  15. package/dist/errors.d.ts.map +1 -1
  16. package/dist/errors.js +40 -12
  17. package/dist/errors.js.map +1 -1
  18. package/dist/lib/equalityIndex.d.ts +334 -0
  19. package/dist/lib/equalityIndex.d.ts.map +1 -0
  20. package/dist/lib/equalityIndex.js +552 -0
  21. package/dist/lib/equalityIndex.js.map +1 -0
  22. package/dist/lib/exportManifest.d.ts +9 -0
  23. package/dist/lib/exportManifest.d.ts.map +1 -1
  24. package/dist/lib/exportManifest.js +9 -0
  25. package/dist/lib/exportManifest.js.map +1 -1
  26. package/dist/lib/importTar.d.ts +18 -2
  27. package/dist/lib/importTar.d.ts.map +1 -1
  28. package/dist/lib/importTar.js +77 -4
  29. package/dist/lib/importTar.js.map +1 -1
  30. package/dist/lib/paths.d.ts +33 -0
  31. package/dist/lib/paths.d.ts.map +1 -1
  32. package/dist/lib/paths.js +28 -0
  33. package/dist/lib/paths.js.map +1 -1
  34. package/dist/lib/resourceFileName.d.ts +43 -0
  35. package/dist/lib/resourceFileName.d.ts.map +1 -1
  36. package/dist/lib/resourceFileName.js +63 -0
  37. package/dist/lib/resourceFileName.js.map +1 -1
  38. package/dist/plugin.d.ts.map +1 -1
  39. package/dist/plugin.js +5 -2
  40. package/dist/plugin.js.map +1 -1
  41. package/dist/requests/ChunkRequest.d.ts +104 -0
  42. package/dist/requests/ChunkRequest.d.ts.map +1 -0
  43. package/dist/requests/ChunkRequest.js +396 -0
  44. package/dist/requests/ChunkRequest.js.map +1 -0
  45. package/dist/requests/CollectionRequest.d.ts +22 -5
  46. package/dist/requests/CollectionRequest.d.ts.map +1 -1
  47. package/dist/requests/CollectionRequest.js +192 -11
  48. package/dist/requests/CollectionRequest.js.map +1 -1
  49. package/dist/requests/ResourceRequest.d.ts.map +1 -1
  50. package/dist/requests/ResourceRequest.js +20 -0
  51. package/dist/requests/ResourceRequest.js.map +1 -1
  52. package/dist/requests/SpaceRequest.d.ts +1 -0
  53. package/dist/requests/SpaceRequest.d.ts.map +1 -1
  54. package/dist/requests/SpaceRequest.js +21 -1
  55. package/dist/requests/SpaceRequest.js.map +1 -1
  56. package/dist/routes.d.ts.map +1 -1
  57. package/dist/routes.js +21 -0
  58. package/dist/routes.js.map +1 -1
  59. package/dist/types.d.ts +165 -2
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +19 -18
  62. package/src/backends/filesystem.ts +924 -75
  63. package/src/backends/postgres.ts +1114 -32
  64. package/src/backends/postgresSchema.ts +69 -57
  65. package/src/errors.ts +44 -12
  66. package/src/lib/equalityIndex.ts +769 -0
  67. package/src/lib/importTar.ts +107 -4
  68. package/src/lib/paths.ts +48 -0
  69. package/src/lib/resourceFileName.ts +69 -0
  70. package/src/plugin.ts +5 -2
  71. package/src/requests/ChunkRequest.ts +478 -0
  72. package/src/requests/CollectionRequest.ts +221 -12
  73. package/src/requests/ResourceRequest.ts +20 -0
  74. package/src/requests/SpaceRequest.ts +25 -1
  75. package/src/routes.ts +46 -0
  76. package/src/types.ts +175 -1
package/src/types.ts CHANGED
@@ -41,6 +41,13 @@ import type {
41
41
  BlindedIndexQuery,
42
42
  BlindedIndexQueryPage
43
43
  } from './lib/blindedIndex.js'
44
+ import type {
45
+ EqualityQuery,
46
+ EqualityQueryPage,
47
+ EqualityCandidate,
48
+ EqualityValue,
49
+ NormalizedIndexDeclaration
50
+ } from './lib/equalityIndex.js'
44
51
 
45
52
  // The shared WAS wire model now lives in `@interop/storage-core`. Import the
46
53
  // shapes referenced by the `StorageBackend` contract below, and re-export the
@@ -64,6 +71,17 @@ import type {
64
71
  // contract (`queryByBlindedIndex`) from this one module.
65
72
  export type { BlindedIndexQuery, BlindedIndexQueryPage }
66
73
 
74
+ // Surface the equality-index query shapes referenced by the `StorageBackend`
75
+ // contract (`queryByEquality` / `findEqualityUniqueViolation`) from this one
76
+ // module, alongside the blinded-index ones.
77
+ export type {
78
+ EqualityQuery,
79
+ EqualityQueryPage,
80
+ EqualityCandidate,
81
+ EqualityValue,
82
+ NormalizedIndexDeclaration
83
+ }
84
+
67
85
  // Surface the reused @interop/data-integrity-core types from this one module.
68
86
  export type {
69
87
  IDID,
@@ -91,6 +109,7 @@ export type {
91
109
  ResourceSummary,
92
110
  CollectionResourcesList,
93
111
  CollectionDescription,
112
+ CollectionIndexDeclaration,
94
113
  CollectionEncryption,
95
114
  CollectionEncryptionEpoch,
96
115
  CollectionEncryptionRecipient,
@@ -608,12 +627,30 @@ export interface StorageBackend {
608
627
  * (see `lib/blindedIndex.ts`). Conflicts require `unique: true` on both
609
628
  * sides, and a document keeping its own unique attribute across an update
610
629
  * never self-conflicts.
630
+ *
631
+ * A backend carrying the `equality-query` feature enforces the analogous
632
+ * plaintext unique-attribute invariant when the request layer passes
633
+ * `uniqueIndexes` (the target Collection's normalized `unique: true`
634
+ * declarations, resolved from its control-plane description): a write whose
635
+ * extracted value for a `unique` content-sourced attribute is already claimed
636
+ * by a different live Resource in the same Collection rejects with
637
+ * `UniqueAttributeConflictError` (409), evaluated atomically with the write
638
+ * (see `lib/equalityIndex.ts`). Unlike the blinded invariant this is
639
+ * Collection-level, so a conflict does not require the other side to opt in.
611
640
  */
612
641
  writeResource(options: {
613
642
  spaceId: string
614
643
  collectionId: string
615
644
  resourceId: string
616
645
  input: ResourceInput
646
+ /**
647
+ * The target Collection's normalized `unique: true` index declarations,
648
+ * passed by the request layer only when the Collection declares any. The
649
+ * backend enforces the plaintext unique-attribute claim atomically with the
650
+ * write (409 `UniqueAttributeConflictError`), for the content-sourced
651
+ * entries a content write can claim.
652
+ */
653
+ uniqueIndexes?: NormalizedIndexDeclaration[]
617
654
  /**
618
655
  * DID of the party whose capability invocation authorized this write (the
619
656
  * signing key's DID, fragment stripped). Recorded as the Resource's
@@ -645,7 +682,9 @@ export interface StorageBackend {
645
682
  * Deletes a Resource. When `ifMatch` is supplied (`conditional-writes`), the
646
683
  * delete proceeds only if the Resource's current ETag matches, evaluated
647
684
  * atomically with the removal; a mismatch rejects with `precondition-failed`
648
- * (412). Without it, the delete is unconditional and idempotent.
685
+ * (412). Without it, the delete is unconditional and idempotent. Deleting a
686
+ * Resource cascade-deletes any chunks stored under it (the `chunked-streams`
687
+ * feature), so chunks never outlive their parent.
649
688
  */
650
689
  deleteResource(options: {
651
690
  spaceId: string
@@ -683,6 +722,15 @@ export interface StorageBackend {
683
722
  collectionId: string
684
723
  resourceId: string
685
724
  custom: ResourceMetadataCustom | Record<string, unknown>
725
+ /**
726
+ * The target Collection's normalized `unique: true` index declarations,
727
+ * passed by the request layer only when the Collection declares any. The
728
+ * backend enforces the plaintext unique-attribute claim atomically with the
729
+ * metadata write (409 `UniqueAttributeConflictError`), for the
730
+ * custom-sourced entries a metadata write can claim (see
731
+ * `lib/equalityIndex.ts`).
732
+ */
733
+ uniqueIndexes?: NormalizedIndexDeclaration[]
686
734
  /**
687
735
  * The client-declared key epoch (the `key-epochs` feature), a sibling of
688
736
  * `custom`. Unlike `custom` (full replacement), an OMITTED `epoch`
@@ -694,6 +742,81 @@ export interface StorageBackend {
694
742
  ifNoneMatch?: boolean
695
743
  }): Promise<{ metaVersion: number } | undefined>
696
744
 
745
+ /**
746
+ * Writes one chunk of a chunked Resource (the `chunked-streams` feature),
747
+ * keyed by `(spaceId, collectionId, resourceId, chunkIndex)`. The chunk body
748
+ * is opaque bytes + content-type -- stored exactly like a binary Resource
749
+ * representation (same upload cap / quota guards); the server never parses
750
+ * it. The parent Resource MUST already exist: writing a chunk of an absent
751
+ * Resource rejects with `ResourceNotFoundError` (404), so orphan chunks
752
+ * cannot accumulate. An upsert per chunk, bumping the chunk's own monotonic
753
+ * `version` (its ETag validator, independent of the parent's); an `ifMatch` /
754
+ * `ifNoneMatch` precondition is evaluated on that chunk version atomically
755
+ * with the write (`precondition-failed` 412 on mismatch).
756
+ */
757
+ writeChunk(options: {
758
+ spaceId: string
759
+ collectionId: string
760
+ resourceId: string
761
+ chunkIndex: number
762
+ input: ResourceInput
763
+ ifMatch?: string
764
+ ifNoneMatch?: boolean
765
+ }): Promise<{ version: number }>
766
+ /** Reads a chunk's bytes; rejects with `ResourceNotFoundError` when absent. */
767
+ getChunk(options: {
768
+ spaceId: string
769
+ collectionId: string
770
+ resourceId: string
771
+ chunkIndex: number
772
+ }): Promise<ResourceResult>
773
+ /**
774
+ * Reads a chunk's stored content-type / size / version (the HEAD payload
775
+ * headers). Resolves `undefined` when the chunk is absent.
776
+ */
777
+ getChunkMetadata(options: {
778
+ spaceId: string
779
+ collectionId: string
780
+ resourceId: string
781
+ chunkIndex: number
782
+ }): Promise<
783
+ { contentType: string; size: number; version?: number } | undefined
784
+ >
785
+ /**
786
+ * Deletes one chunk. Resolves `true` when a chunk was removed and `false`
787
+ * when none was stored at that index (the handler 404s on `false` -- unlike
788
+ * `deleteResource`, chunk deletes are not silently idempotent, mirroring the
789
+ * EDV chunk contract). An `ifMatch` precondition is evaluated atomically
790
+ * with the removal (`precondition-failed` 412 on mismatch).
791
+ */
792
+ deleteChunk(options: {
793
+ spaceId: string
794
+ collectionId: string
795
+ resourceId: string
796
+ chunkIndex: number
797
+ ifMatch?: string
798
+ }): Promise<boolean>
799
+ /**
800
+ * Lists a Resource's stored chunks in ascending `index` order -- the
801
+ * discovery/reassembly listing (the server never reassembles; a reader
802
+ * learns the chunk set here). Resolves an empty listing when the Resource
803
+ * has no chunks (including when the Resource itself is absent -- existence
804
+ * is the parent routes' concern).
805
+ */
806
+ listChunks(options: {
807
+ spaceId: string
808
+ collectionId: string
809
+ resourceId: string
810
+ }): Promise<{
811
+ count: number
812
+ chunks: Array<{
813
+ index: number
814
+ size: number
815
+ contentType: string
816
+ version?: number
817
+ }>
818
+ }>
819
+
697
820
  /**
698
821
  * OPTIONAL replication change feed (the `changes` query profile.
699
822
  * Returns the Collection's JSON-document
@@ -777,6 +900,57 @@ export interface StorageBackend {
777
900
  cursor?: string
778
901
  }): Promise<{ count: number } | BlindedIndexQueryPage>
779
902
 
903
+ /**
904
+ * OPTIONAL plaintext equality query (the `equality` query profile; the
905
+ * `equality-query` feature token). Evaluates an equality query -- `equals`
906
+ * (OR across elements of an AND within each element's `{name: value}` pairs)
907
+ * or `has` (every named attribute present with an indexable value) -- over
908
+ * the attributes the server extracts from the Collection's live Resources per
909
+ * the `indexes` declaration. `indexes` is the NORMALIZED declaration array:
910
+ * the request layer resolves it from the control-plane description (a
911
+ * data-plane backend does not hold the description) and passes it in. Matching
912
+ * is strict JSON equality (no coercion); a content-sourced attribute reads a
913
+ * JSON Resource's stored content, a custom-sourced one reads the `custom`
914
+ * metadata object (so blobs are queryable too). With `count`, resolves only
915
+ * the match total; otherwise a page of matching documents (`{ id, data?,
916
+ * custom? }`) in ascending `resourceId` order, paginated with the standard
917
+ * opaque cursor (`cursor` present iff `hasMore`; a malformed one rejects with
918
+ * `invalid-cursor` 400). Both first-party backends answer through
919
+ * `lib/equalityIndex.ts` so semantics cannot drift.
920
+ *
921
+ * OPTIONAL: a backend that omits this method does not serve the profile, and
922
+ * the request layer returns `unsupported-operation` (501). The Space and
923
+ * Collection are guaranteed to exist by the request layer.
924
+ */
925
+ queryByEquality?(options: {
926
+ spaceId: string
927
+ collectionId: string
928
+ query: EqualityQuery
929
+ indexes: NormalizedIndexDeclaration[]
930
+ count?: boolean
931
+ limit?: number
932
+ cursor?: string
933
+ }): Promise<{ count: number } | EqualityQueryPage>
934
+
935
+ /**
936
+ * OPTIONAL declare-time uniqueness scan for the `equality` profile: given the
937
+ * Collection's normalized `unique: true` declarations, scans its already-
938
+ * stored live Resources for two DIFFERENT Resources that claim the same
939
+ * `(name, value)`, resolving the first such `{ name, value }` or `undefined`
940
+ * when none exists. The request layer runs it when a Collection update ADDS a
941
+ * unique claim, rejecting a found violation with `id-conflict` (409) so a
942
+ * unique claim is never acknowledged over already-conflicting data.
943
+ *
944
+ * OPTIONAL: paired with `queryByEquality` on backends carrying the
945
+ * `equality-query` feature. The Space and Collection are guaranteed to exist
946
+ * by the request layer.
947
+ */
948
+ findEqualityUniqueViolation?(options: {
949
+ spaceId: string
950
+ collectionId: string
951
+ indexes: NormalizedIndexDeclaration[]
952
+ }): Promise<{ name: string; value: EqualityValue } | undefined>
953
+
780
954
  /**
781
955
  * Access-control policy documents. The level is selected by which ids are
782
956
  * present: Space (`spaceId`), Collection (`+ collectionId`), or Resource